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");
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;
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
@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).
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
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();
//------------------------------------------------------------------------------
//-------------------------------------------------- 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;
}
}
+//------------------------------------------------------------------------------
+//-------------------------------------------------- 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()
//------------------------------------------------------------------------------
//
// 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();
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);
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);
}
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;
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();
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);
}