From ad7fdc865803176f1dd1696960073f616cfa3fda Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Testillano Date: Fri, 10 Apr 2015 02:33:52 +0200 Subject: [PATCH] Stack selection by application-id (configurable), and minor fixes --- example/diameter/launcher/main.cpp | 2 +- include/anna/diameter/codec/EngineImpl.hpp | 40 ++++++++++++++++++---- include/anna/diameter/codec/Message.hpp | 15 ++++++-- source/diameter/codec/EngineImpl.cpp | 3 +- source/diameter/codec/Message.cpp | 40 +++++++++++++--------- source/diameter/stack/Dictionary.cpp | 2 +- 6 files changed, 74 insertions(+), 28 deletions(-) diff --git a/example/diameter/launcher/main.cpp b/example/diameter/launcher/main.cpp index 5a5db42..0b5ca7e 100644 --- a/example/diameter/launcher/main.cpp +++ b/example/diameter/launcher/main.cpp @@ -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"); diff --git a/include/anna/diameter/codec/EngineImpl.hpp b/include/anna/diameter/codec/EngineImpl.hpp index 049906b..776a5fb 100644 --- a/include/anna/diameter/codec/EngineImpl.hpp +++ b/include/anna/diameter/codec/EngineImpl.hpp @@ -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). diff --git a/include/anna/diameter/codec/Message.hpp b/include/anna/diameter/codec/Message.hpp index 2b65175..c8dab7c 100644 --- a/include/anna/diameter/codec/Message.hpp +++ b/include/anna/diameter/codec/Message.hpp @@ -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 diff --git a/source/diameter/codec/EngineImpl.cpp b/source/diameter/codec/EngineImpl.cpp index c28a3ed..45a85ed 100644 --- a/source/diameter/codec/EngineImpl.cpp +++ b/source/diameter/codec/EngineImpl.cpp @@ -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; } diff --git a/source/diameter/codec/Message.cpp b/source/diameter/codec/Message.cpp index 6f906f8..4938a03 100644 --- a/source/diameter/codec/Message.cpp +++ b/source/diameter/codec/Message.cpp @@ -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 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(); diff --git a/source/diameter/stack/Dictionary.cpp b/source/diameter/stack/Dictionary.cpp index 0d48b5d..821659c 100644 --- a/source/diameter/stack/Dictionary.cpp +++ b/source/diameter/stack/Dictionary.cpp @@ -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); } -- 2.20.1