Ensures normalization on waitfe/fc-xml operations
[anna.git] / source / diameter / codec / Message.cpp
index 3a989cf..a686c71 100644 (file)
@@ -1,37 +1,9 @@
-// ANNA - Anna is Not Nothingness Anymore
-//
-// (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
-//
-// http://redmine.teslayout.com/projects/anna-suite
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-//
-//     * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//     *  Neither the name of the copyright holder nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-//
-// Authors: eduardo.ramos.testillano@gmail.com
-//          cisco.tierra@gmail.com
+// ANNA - Anna is Not Nothingness Anymore                                                         //
+//                                                                                                //
+// (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
+//                                                                                                //
+// 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 //
 
 
 // Local
@@ -44,6 +16,7 @@
 #include <anna/diameter/codec/functions.hpp> // REQUIRED_WORDS
 #include <anna/diameter/codec/OamModule.hpp>
 #include <anna/diameter/codec/Engine.hpp>
+#include <anna/diameter/codec/EngineManager.hpp>
 #include <anna/diameter/stack/Avp.hpp>
 #include <anna/diameter/stack/Format.hpp>
 #include <anna/diameter/stack/Dictionary.hpp>
@@ -84,7 +57,7 @@ const U8 Message::TBitMask(0x10);
 //------------------------------------------------------------------------------
 //----------------------------------------------------------- Message::Message()
 //------------------------------------------------------------------------------
-Message::Message() : a_forCode(true) {
+Message::Message(Engine *engine) : a_engine(engine), a_forCode(true) {
   initialize();
 }
 
@@ -92,7 +65,7 @@ Message::Message() : a_forCode(true) {
 //------------------------------------------------------------------------------
 //----------------------------------------------------------- Message::Message()
 //------------------------------------------------------------------------------
-Message::Message(CommandId id) : a_forCode(true) {
+Message::Message(CommandId id, Engine *engine) : a_engine(engine), a_forCode(true) {
   initialize();
   setId(id);
 }
@@ -106,11 +79,34 @@ Message::~Message() {
 }
 
 
+//------------------------------------------------------------------------------
+//--------------------------------------------------------- Message::setEngine()
+//------------------------------------------------------------------------------
+void Message::setEngine(Engine *engine) throw() {
+
+  if (!engine) {
+    LOGWARNING(anna::Logger::warning("Ignored: you must assign a valid codec engine. If you want to set NULL engine, clear the message", ANNA_FILE_LOCATION));
+    return;
+  }
+
+  if (a_engine && (engine != a_engine)) {
+    LOGWARNING(anna::Logger::warning("Ignored: it is not a good practice to change the codec engine once assigned. Clear the message first to set the engine again.", ANNA_FILE_LOCATION));
+    return;
+  }
+
+  a_engine = engine;
+}
+
+
 //------------------------------------------------------------------------------
 //--------------------------------------------------------- Message::getEngine()
 //------------------------------------------------------------------------------
 Engine * Message::getEngine() const throw(anna::RuntimeException) {
-  return a_engine ? a_engine : (a_engine = anna::functions::component <Engine> (ANNA_FILE_LOCATION));
+  if(!a_engine)
+    throw anna::RuntimeException("Invalid codec engine reference (NULL). Use setEngine() to set the corresponding codec engine", ANNA_FILE_LOCATION);
+
+  return a_engine;
+
 }
 
 
@@ -118,7 +114,6 @@ Engine * Message::getEngine() const throw(anna::RuntimeException) {
 //-------------------------------------------------------- Message::initialize()
 //------------------------------------------------------------------------------
 void Message::initialize() throw() {
-  a_engine = NULL;
   a_version = 1;
   a_id = CommandId(0, false);
   a_flags = 0x00;
@@ -134,7 +129,7 @@ void Message::initialize() throw() {
 //------------------------------------------------------------------------------
 //------------------------------------------------------------- Message::clear()
 //------------------------------------------------------------------------------
-void Message::clear() throw(anna::RuntimeException) {
+void Message::clear(bool resetEngine) throw(anna::RuntimeException) {
   for(avp_iterator it = avp_begin(); it != avp_end(); it++) { /*avp(it)->clear(); */getEngine()->releaseAvp(Avp::avp(it)); }
 
   a_avps.clear();
@@ -143,6 +138,7 @@ void Message::clear() throw(anna::RuntimeException) {
   a_finds.clear();
   // Initialize:
   initialize();
+  if (resetEngine) a_engine = NULL;
 }
 
 
@@ -154,7 +150,9 @@ bool Message::flagsOK(int &rc) const throw() {
   const stack::Command *stackCommand = getStackCommand();
 
   if(!stackCommand) {
-    anna::Logger::error("Impossible to decide if flags are correct because stack command is not identified. Assume flags ok", ANNA_FILE_LOCATION);
+    std::string msg = "Impossible to decide if flags are correct because stack command is not identified. Assume flags ok for Message ";
+    msg += anna::diameter::functions::commandIdAsPairString(a_id);
+    anna::Logger::error(msg, ANNA_FILE_LOCATION);
     //rc = helpers::base::AVPVALUES__Result_Code::?????;
     return true;
   };
@@ -169,12 +167,16 @@ bool Message::flagsOK(int &rc) const throw() {
   if(stackCommand->isRequest() != isRequest()) ok = false;  // en teoria es imposible salir por aqui: blindado en la dtd
 
   if(isRequest() && errorBit()) {
-    anna::Logger::error("E(rror) bit is not allowed at diameter requests", ANNA_FILE_LOCATION);
+    std::string msg = "E(rror) bit is not allowed at diameter requests as ";
+    msg += stackCommand->getName();
+    anna::Logger::error(msg, ANNA_FILE_LOCATION);
     ok = false;
   }
 
   if(isAnswer() && potentiallyReTransmittedMessageBit()) {
-    anna::Logger::error("T(Potentially re-transmitted message) bit is not allowed at diameter answers", ANNA_FILE_LOCATION);
+    std::string msg = "T(Potentially re-transmitted message) bit is not allowed at diameter answers as ";
+    msg += stackCommand->getName();
+    anna::Logger::error(msg, ANNA_FILE_LOCATION);
     ok = false;
   }
 
@@ -189,7 +191,9 @@ bool Message::flagsOK(int &rc) const throw() {
   //      is set to one (1) or the bits in the Diameter header are set
   //      incorrectly.
   if((a_flags & 0x0f) != 0x00) {
-    anna::Logger::error("Any (or more than one) of the reserved message flags bit has been activated. Reserved bits must be null", ANNA_FILE_LOCATION);
+    std::string msg = "Any (or more than one) of the reserved message flags bit has been activated. Reserved bits must be null. Message is ";
+    msg += stackCommand->getName();
+    anna::Logger::error(msg, ANNA_FILE_LOCATION);
     rc = helpers::base::AVPVALUES__Result_Code::DIAMETER_INVALID_BIT_IN_HEADER;
     return false;
   }
@@ -201,9 +205,7 @@ bool Message::flagsOK(int &rc) const throw() {
 //------------------------------------------------------------------------------
 //------------------------------------------------------------- Message::setId()
 //------------------------------------------------------------------------------
-void Message::setId(CommandId id, bool _clear) throw(anna::RuntimeException) {
-  // Clear class content:
-  if(_clear) clear();
+void Message::setId(CommandId id) throw(anna::RuntimeException) {
 
   // Id assignment:
   a_id = id;
@@ -227,6 +229,27 @@ void Message::setId(const char *name) throw(anna::RuntimeException) {
 }
 
 
+//------------------------------------------------------------------------------
+//-------------------------------------------------- Message::setApplicationId()
+//------------------------------------------------------------------------------
+void Message::setApplicationId(U32 aid) throw(anna::RuntimeException) {
+  a_applicationId = aid;
+
+  // Automatic engine configuration:
+  if (a_engine) return;
+
+  // Codec engine manager (a multithreaded application, normally does not achieve this point, because
+  // messages are prepared for each interface with the corresponding codec engine)
+  anna::diameter::codec::EngineManager &em = anna::diameter::codec::EngineManager::instantiate();
+  if (em.size() == 0) return;
+  if (em.selectFromApplicationId()) {
+    Engine *monostackEngine = em.getMonoStackCodecEngine();
+    if (monostackEngine) { a_engine = monostackEngine; return; }
+    a_engine = em.getCodecEngine(aid);
+  }
+}
+
+
 //------------------------------------------------------------------------------
 //------------------------------------------------------------ Message::addAvp()
 //------------------------------------------------------------------------------
@@ -235,6 +258,17 @@ Avp * Message::addAvp(const char *name) throw(anna::RuntimeException) {
 }
 
 
+//------------------------------------------------------------------------------
+//------------------------------------------------------------ Message::addAvp()
+//------------------------------------------------------------------------------
+Avp * Message::addAvp(Avp * avp) throw() {
+  if(!avp) return NULL;
+  if (avp->getEngine() != getEngine()) return NULL;
+  addChild(avp);
+  return avp;
+}
+
+
 //------------------------------------------------------------------------------
 //--------------------------------------------------------- Message::removeAvp()
 //------------------------------------------------------------------------------
@@ -279,12 +313,12 @@ U24 Message::getLength() const throw() {
 void Message::decode(const anna::DataBlock &db, Message *ptrAnswer) throw(anna::RuntimeException) {
   // Trace
   LOGDEBUG(
-    anna::xml::Node root("Message::decode");
-    std::string trace = "DataBlock to decode:\n";
-    trace += db.asString();
-    anna::Logger::debug(trace, ANNA_FILE_LOCATION);
+      anna::xml::Node root("Message::decode");
+  std::string trace = "DataBlock to decode:\n";
+  trace += db.asString();
+  anna::Logger::debug(trace, ANNA_FILE_LOCATION);
   );
-  clear();
+  clear(false /* respect engine */);
   // EXCEPTION MANAGEMENT IN THIS METHOD
   // ===================================
   // DECODE PHASE
@@ -294,7 +328,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 +365,9 @@ 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 */));
+  a_id = CommandId(code, requestBit() /* based on a_flags */);
 
-  a_applicationId = DECODE4BYTES_INDX_VALUETYPE(buffer, 8, U32);
+  setApplicationId(DECODE4BYTES_INDX_VALUETYPE(buffer, 8, U32)); // centralize set, because it could be used for stack selection.
 
   a_hopByHop = DECODE4BYTES_INDX_VALUETYPE(buffer, 12, U32);
 
@@ -377,14 +411,14 @@ void Message::decode(const anna::DataBlock &db, Message *ptrAnswer) throw(anna::
 
   while(avpPos < dataBytes) {
     try {
-      avp =  getEngine()->allocateAvp();
+      avp =  getEngine()->createAvp(NULL);
       db_aux.assign(startData + avpPos, dataBytes - avpPos /* is valid to pass total length (indeed i don't know the real avp length) because it will be limited and this has deep copy disabled (no memory is reserved) */);
       avp -> decode(db_aux, parent, answer);
     } catch(anna::RuntimeException &ex) {
       getEngine()->releaseAvp(avp);
       LOGWARNING(
-        anna::Logger::warning(ex.getText(), ANNA_FILE_LOCATION);
-        anna::Logger::warning("Although a decoding error was found, validation could be checked because message could be enough for the application", ANNA_FILE_LOCATION);
+          anna::Logger::warning(ex.getText(), ANNA_FILE_LOCATION);
+      anna::Logger::warning("Although a decoding error was found, validation could be checked because message could be enough for the application", ANNA_FILE_LOCATION);
       );
       break;
     }
@@ -400,16 +434,16 @@ void Message::decode(const anna::DataBlock &db, Message *ptrAnswer) throw(anna::
 
   // Trace
   LOGDEBUG(
-    std::string trace = "Message decoded:\n";
-    trace += asXMLString();
-    anna::Logger::debug(trace, ANNA_FILE_LOCATION);
+      std::string trace = "Message decoded:\n";
+  trace += asXMLString();
+  anna::Logger::debug(trace, ANNA_FILE_LOCATION);
   );
   // Post-Validation
   Engine::ValidationMode::_v vmode = getEngine()->getValidationMode();
 
   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);
 }
 
 
@@ -460,33 +494,35 @@ int Message::getResultCode() const throw() {
 //------------------------------------------------------------------------------
 //------------------------------------------------------ Message::setFailedAvp()
 //------------------------------------------------------------------------------
-void Message::setFailedAvp(const parent_t &parent, AvpId wrong) throw(anna::RuntimeException) {
+void Message::setFailedAvp(const parent_t &parent, AvpId wrong, const char *wrongName) throw(anna::RuntimeException) {
 
   if(isRequest()) return;
 
-// RFC 6733:
-//
-//  7.5.  Failed-AVP AVP
-//
-//     The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
-//     debugging information in cases where a request is rejected or not
-//     fully processed due to erroneous information in a specific AVP.  The
-//     value of the Result-Code AVP will provide information on the reason
-//     for the Failed-AVP AVP.  A Diameter answer message SHOULD contain an
-//     instance of the Failed-AVP AVP that corresponds to the error
-//     indicated by the Result-Code AVP.  For practical purposes, this
-//     Failed-AVP would typically refer to the first AVP processing error
-//     that a Diameter node encounters.
+  // RFC 6733:
+  //
+  //  7.5.  Failed-AVP AVP
+  //
+  //     The Failed-AVP AVP (AVP Code 279) is of type Grouped and provides
+  //     debugging information in cases where a request is rejected or not
+  //     fully processed due to erroneous information in a specific AVP.  The
+  //     value of the Result-Code AVP will provide information on the reason
+  //     for the Failed-AVP AVP.  A Diameter answer message SHOULD contain an
+  //     instance of the Failed-AVP AVP that corresponds to the error
+  //     indicated by the Result-Code AVP.  For practical purposes, this
+  //     Failed-AVP would typically refer to the first AVP processing error
+  //     that a Diameter node encounters.
 
   // Although the Failed-AVP definition has cardinality 1* and Failed-AVP itself is defined in
   // most of the command codes as *[Failed-AVP], i think this is not a deliberate ambiguity.
   // Probably the RFC wants to give freedom to the application layer, but it is recommended to
   // have only one child (wrong avp) inside a unique message Failed-AVP to ease the Result-Code
-  // correspondence. Anyway, this behaviour could be easily  opened commenting condition block (*).
+  // correspondence. Anyway, this behaviour could be easily  opened by mean 'setSingleFailedAVP(false)'
   Avp *theFailedAvp = getAvp(helpers::base::AVPID__Failed_AVP, 1, anna::Exception::Mode::Ignore);
   if (theFailedAvp) {
-       LOGDEBUG(anna::Logger::debug("Failed-AVP has already been added. RFC 6733 Section 7.5 recommends to store only the first error found", ANNA_FILE_LOCATION));
-    return;
+    if (getEngine()->getSingleFailedAVP()) {
+      LOGDEBUG(anna::Logger::debug("Failed-AVP has already been added. RFC 6733 Section 7.5 recommends to store only the first error found", ANNA_FILE_LOCATION));
+      return;
+    }
   }
 
   // Section 7.5 RFC 6733: A Diameter message SHOULD contain one Failed-AVP AVP
@@ -494,17 +530,17 @@ void Message::setFailedAvp(const parent_t &parent, AvpId wrong) throw(anna::Runt
   Avp *leaf = theFailedAvp;
 
   LOGDEBUG(
-    std::string msg = "Adding to Failed-AVP, the wrong avp ";
-    msg += anna::diameter::functions::avpIdAsPairString(wrong);
-    msg += " found inside ";
-    msg += parent.asString();
+      std::string msg = "Adding to Failed-AVP, the wrong avp ";
+  msg += wrongName ? wrongName : (anna::diameter::functions::avpIdAsPairString(wrong));
+  msg += " found inside ";
+  msg += parent.asString();
 
-    anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+  anna::Logger::debug(msg, ANNA_FILE_LOCATION);
   );
 
   std::vector<AvpId>::const_iterator it;
   for(it = parent.AvpsId.begin(); it != parent.AvpsId.end(); it++)
-       leaf = leaf->addAvp(*it);
+    leaf = leaf->addAvp(*it);
 
   leaf->addAvp(wrong);
 }
@@ -513,7 +549,7 @@ void Message::setFailedAvp(const parent_t &parent, AvpId wrong) throw(anna::Runt
 //------------------------------------------------------------------------------
 //----------------------------------------------- Message::setStandardToAnswer()
 //------------------------------------------------------------------------------
-void Message::setStandardToAnswer(const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode) throw() {
+void Message::setStandardToAnswer(const Message &request, const std::string &originHost, const std::string &originRealm, int resultCode) throw(anna::RuntimeException) {
   if(!request.getId().second) return;
 
   // Message header:
@@ -521,8 +557,10 @@ void Message::setStandardToAnswer(const Message &request, const std::string &ori
   // Session-Id if exists:
   const Avp *reqSessionId = request.getAvp(helpers::base::AVPID__Session_Id, 1, anna::Exception::Mode::Ignore);
 
+  LOGDEBUG(anna::Logger::debug("Check answer message AVPs Session-Id, Origin-Host and Origin-Realm => replace them if missing, with request session-id & node configuration:", ANNA_FILE_LOCATION));
+
   if(reqSessionId)
-       if(!getAvp(helpers::base::AVPID__Session_Id, 1, anna::Exception::Mode::Ignore))
+    if(!getAvp(helpers::base::AVPID__Session_Id, 1, anna::Exception::Mode::Ignore))
       addAvp(helpers::base::AVPID__Session_Id)->getUTF8String()->setValue(reqSessionId->getUTF8String()->getValue());
 
   // Origin-Host & Realm
@@ -542,9 +580,9 @@ void Message::setStandardToAnswer(const Message &request, const std::string &ori
   // Fix:
   fix();
   LOGDEBUG(
-    std::string msg = "Completed answer:\n";
-    msg += asXMLString();
-    anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+      std::string msg = "Completed answer:\n";
+  msg += asXMLString();
+  anna::Logger::debug(msg, ANNA_FILE_LOCATION);
   );
 }
 
@@ -645,7 +683,7 @@ const anna::DataBlock & Message::code() throw(anna::RuntimeException) {
   // Pre-Validation
   Engine::ValidationMode::_v vmode = getEngine()->getValidationMode();
 
-  if((vmode == Engine::ValidationMode::BeforeCoding) || (vmode == Engine::ValidationMode::Always)) {
+  if((vmode == Engine::ValidationMode::BeforeEncoding) || (vmode == Engine::ValidationMode::Always)) {
     if(!valid())
       throw anna::RuntimeException("Try to encode an invalid message. See previous report on warning-level traces", ANNA_FILE_LOCATION);
   }
@@ -653,13 +691,13 @@ const anna::DataBlock & Message::code() throw(anna::RuntimeException) {
   // Pre-Fixing
   Engine::FixMode::_v fmode = getEngine()->getFixMode();
 
-  if((fmode == Engine::FixMode::BeforeCoding) || (fmode == Engine::FixMode::Always)) fix();
+  if((fmode == Engine::FixMode::BeforeEncoding) || (fmode == Engine::FixMode::Always)) fix();
 
   // Trace
   LOGDEBUG(
-    std::string trace = "Message to code:\n";
-    trace += asXMLString();
-    anna::Logger::debug(trace, ANNA_FILE_LOCATION);
+      std::string trace = "Message to code:\n";
+  trace += asXMLString();
+  anna::Logger::debug(trace, ANNA_FILE_LOCATION);
   );
   // Memory allocation
   U24 length = getLength();
@@ -713,35 +751,40 @@ const anna::DataBlock & Message::code() throw(anna::RuntimeException) {
 
   // Trace
   LOGDEBUG(
-    std::string trace = "DataBlock encoded:\n";
-    trace += a_forCode.asString();
-//      trace += "\nAs continuous hexadecimal string:\n";
-//      trace += anna::functions::asHexString(a_forCode);
-    anna::Logger::debug(trace, ANNA_FILE_LOCATION);
+      std::string trace = "DataBlock encoded:\n";
+  trace += a_forCode.asString();
+  //      trace += "\nAs continuous hexadecimal string:\n";
+  //      trace += anna::functions::asHexString(a_forCode);
+  anna::Logger::debug(trace, ANNA_FILE_LOCATION);
   );
   return a_forCode;
 }
 
-
 //------------------------------------------------------------------------------
-//----------------------------------------------------- Message::fromXMLString()
+//------------------------------------------------------- Message::loadXMLFile()
 //------------------------------------------------------------------------------
-void Message::fromXMLString(const std::string &xmlString) throw(anna::RuntimeException) {
-  LOGDEBUG(anna::Logger::debug("Reading diameter message from xml string representation", ANNA_FILE_LOCATION));
-  anna::xml::DocumentMemory xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
-  const anna::xml::Node *rootNode;
-  xmlDocument.initialize(xmlString.c_str());
-  rootNode = xmlDocument.parse(getEngine()->getDTD()); // Parsing: fail here if xml violates dtd
-  LOGDEBUG(anna::Logger::debug("Read OK from XML string representation", ANNA_FILE_LOCATION));
-  fromXML(rootNode);
+void Message::loadXMLFile(const std::string &xmlPathFile) throw(anna::RuntimeException) {
+
+  anna::xml::DocumentFile xmlDocument;
+  anna::diameter::codec::functions::messageXmlDocumentFromXmlFile(xmlDocument, xmlPathFile);
+  fromXML(xmlDocument.getRootNode());
 }
 
+//------------------------------------------------------------------------------
+//----------------------------------------------------- Message::loadXMLString()
+//------------------------------------------------------------------------------
+void Message::loadXMLString(const std::string &xmlString) throw(anna::RuntimeException) {
+
+  anna::xml::DocumentMemory xmlDocument;
+  anna::diameter::codec::functions::messageXmlDocumentFromXmlString(xmlDocument, xmlString);
+  fromXML(xmlDocument.getRootNode());
+}
 
 //------------------------------------------------------------------------------
 //----------------------------------------------------------- Message::fromXML()
 //------------------------------------------------------------------------------
 void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeException) {
-  // <!ATTLIST message version CDATA #IMPLIED name CDATA #IMPLIED code CDATA #IMPLIED flags CDATA #IMPLIED p-bit (yes | no) #IMPLIED e-bit (yes | no) #IMPLIED t-bit (yes | no) #IMPLIED application-id CDATA #REQUIRED hop-by-hop-id CDATA #IMPLIED end-by-end-id CDATA #IMPLIED>
+  // <!ATTLIST message version CDATA #IMPLIED name CDATA #IMPLIED code CDATA #IMPLIED flags CDATA #IMPLIED p-bit (yes | no) #IMPLIED e-bit (yes | no) #IMPLIED t-bit (yes | no) #IMPLIED application-id CDATA #REQUIRED hop-by-hop-id CDATA #IMPLIED end-to-end-id CDATA #IMPLIED>
   const anna::xml::Attribute *version, *name, *code, *flags, *pbit, *ebit, *tbit, *appid, *hbh, *ete;
   version = messageNode->getAttribute("version", false /* no exception */);
   name = messageNode->getAttribute("name", false /* no exception */);
@@ -752,10 +795,13 @@ void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeExc
   tbit = messageNode->getAttribute("t-bit", false /* no exception */);
   appid = messageNode->getAttribute("application-id"); // required
   hbh = messageNode->getAttribute("hop-by-hop-id", false /* no exception */);
-  ete = messageNode->getAttribute("end-by-end-id", false /* no exception */);
+  ete = messageNode->getAttribute("end-to-end-id", false /* no exception */);
   int i_aux;
   unsigned int u_aux;
 
+  // Clear the message
+  clear(false /* respect engine */);
+
   if(version) {
     i_aux = version->getIntegerValue();
 
@@ -768,6 +814,10 @@ void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeExc
     a_version = i_aux;
   }
 
+  // Application-id
+  // This is called before any operation which needs to know about the stack elements (this could set the dictionary)
+  setApplicationId(appid->getIntegerValue());
+
   // Dictionary
   const stack::Dictionary * dictionary = getEngine()->getDictionary();
   const stack::Command *stackCommand = NULL;
@@ -798,6 +848,7 @@ void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeExc
     }
 
     setId(stackCommand->getId());
+
     // 'P', 'E' and 'T' flags:
     bool activateP = pbit ? (pbit->getValue() == "yes") : false;
     bool activateE = ebit ? (ebit->getValue() == "yes") : false;
@@ -848,24 +899,11 @@ void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeExc
     a_flags = i_aux;
     int flagsBCK = a_flags;
     // Final assignments
-    setId(CommandId(u_code, requestBit() /* based on a_flags */));
+    a_id = CommandId(u_code, requestBit() /* based on a_flags */);
     // Flags could have been updated regarding dictionary, but during parsing we must respect xml file:
     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();
@@ -876,7 +914,7 @@ void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeExc
       msg += "': negative values are not allowed";
       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
     }
-    */
+     */
   } else u_aux = 0;
 
   setHopByHop(u_aux);
@@ -891,7 +929,7 @@ void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeExc
       msg += "': negative values are not allowed";
       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
     }
-    */
+     */
   } else u_aux = 0;
 
   setEndToEnd(u_aux);
@@ -908,7 +946,7 @@ void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeExc
     }
 
     try {
-      avp =  getEngine()->allocateAvp();
+      avp =  getEngine()->createAvp(NULL);
       avp -> fromXML(*it);
     } catch(anna::RuntimeException &ex) {
       getEngine()->releaseAvp(avp);
@@ -920,36 +958,11 @@ void Message::fromXML(const anna::xml::Node* messageNode) throw(anna::RuntimeExc
 }
 
 
-//------------------------------------------------------------------------------
-//----------------------------------------------------------- Message::loadXML()
-//------------------------------------------------------------------------------
-void Message::loadXML(const std::string & xmlPathFile) throw(anna::RuntimeException) {
-  LOGDEBUG(
-    std::string trace = "Loading diameter message from file '";
-    trace += xmlPathFile;
-    trace += "'";
-    anna::Logger::debug(trace, ANNA_FILE_LOCATION);
-  );
-  anna::xml::DocumentFile xmlDocument; // has private copy constructor defined but not implemented to avoid inhenrit/copy (is very heavy)
-  const anna::xml::Node *rootNode;
-  xmlDocument.initialize(xmlPathFile.c_str()); // fail here is i/o error
-  rootNode = xmlDocument.parse(getEngine()->getDTD()); // Parsing: fail here if xml violates dtd
-  LOGDEBUG(
-    std::string trace = "Loaded XML file (";
-    trace += xmlPathFile;
-    trace += "):\n";
-    trace += anna::xml::Compiler().apply(rootNode);
-    anna::Logger::debug(trace, ANNA_FILE_LOCATION);
-  );
-  fromXML(rootNode);
-}
-
-
 //------------------------------------------------------------------------------
 //------------------------------------------------------------- Message::asXML()
 //------------------------------------------------------------------------------
 anna::xml::Node* Message::asXML(anna::xml::Node* parent) const throw() {
-  // <!ATTLIST message version CDATA #IMPLIED name CDATA #IMPLIED code CDATA #IMPLIED flags CDATA #IMPLIED application-id CDATA #REQUIRED hop-by-hop-id CDATA #IMPLIED end-by-end-id CDATA #IMPLIED>
+  // <!ATTLIST message version CDATA #IMPLIED name CDATA #IMPLIED code CDATA #IMPLIED flags CDATA #IMPLIED application-id CDATA #REQUIRED hop-by-hop-id CDATA #IMPLIED end-to-end-id CDATA #IMPLIED>
   anna::xml::Node* result = parent->createChild("message");
   // Dictionary stack command:
   const stack::Command *stackCommand = getStackCommand();
@@ -971,7 +984,7 @@ anna::xml::Node* Message::asXML(anna::xml::Node* parent) const throw() {
 
   result->createAttribute("application-id", anna::functions::asString(a_applicationId));
   result->createAttribute("hop-by-hop-id", anna::functions::asString(a_hopByHop));
-  result->createAttribute("end-by-end-id", anna::functions::asString(a_endToEnd));
+  result->createAttribute("end-to-end-id", anna::functions::asString(a_endToEnd));
 
   // Avps:
   for(const_avp_iterator it = avp_begin(); it != avp_end(); it++) {
@@ -985,9 +998,16 @@ anna::xml::Node* Message::asXML(anna::xml::Node* parent) const throw() {
 //------------------------------------------------------------------------------
 //------------------------------------------------------- Message::asXMLString()
 //------------------------------------------------------------------------------
-std::string Message::asXMLString() const throw() {
+std::string Message::asXMLString(bool normalize) const throw() {
   anna::xml::Node root("root");
-  return anna::xml::Compiler().apply(asXML(&root));
+
+  anna::xml::Compiler::Mode::_v mode = normalize ? anna::xml::Compiler::Mode::Sort : anna::xml::Compiler::Mode::Visual;
+  std::string result = anna::xml::Compiler().apply(asXML(&root), mode);
+
+  if (normalize)
+    result.erase(std::remove(result.begin(), result.end(), '\n'), result.end());
+
+  return result;
 }
 
 
@@ -996,5 +1016,6 @@ std::string Message::asXMLString() const throw() {
 //------------------------------------------------------------------------------
 bool Message::isLike(const std::string &pattern) const throw() {
   anna::RegularExpression re(pattern);
-  return re.isLike(asXMLString());
+  return re.isLike(asXMLString(true /* normalize by mean sorting attribute names and removing new lines */));
 }
+