1 // ANNA - Anna is Not Nothingness Anymore //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
9 #ifndef anna_diameter_codec_EngineImpl_hpp
10 #define anna_diameter_codec_EngineImpl_hpp
16 #include <anna/core/RuntimeException.hpp>
17 #include <anna/xml/DTDMemory.hpp>
18 #include <anna/core/util/Recycler.hpp>
20 #include <anna/core/util/Component.hpp>
21 #include <anna/diameter/defines.hpp>
24 //------------------------------------------------------------------------------
25 //---------------------------------------------------------------------- #define
26 //------------------------------------------------------------------------------
44 * General component implementation of a diameter elements factory (messages, avps) and common resources which
45 * configure encode/decode operations behaviour.
47 * Standard inheritance is done over codec::Engine, allocating basic Avp and Message classes.
48 * A child implementation could manage complex Avp classes with new data-part formats. Grouped ones could
49 * allocate new complex Avps through such engine which knows how to allocate this special Avp's (also for
50 * complex Message classes with application-specific setters and getters as credit-control related avps,
51 * or some another context items). For example tme::Avp and tme::Message classes support three new Avp formats:
52 * ISDNNumber, ISDNAddress and Unsigned16. Anyway, main Message/Avp and Engine classes stand for all contexts
53 * included in anna::diameter, that is to say, whole contexts (at the time only TME) will be included in future
54 * when needed apart from the independent namespace version. Thank to this, single threaded applications could
55 * use whole engine in a easy way.
57 * An engine component is associated to a single diameter stack. It is application responsability to use message
58 * container initialized with the correct codec engine depending on the context. There are helpers to do this at
59 * anna::diameter::codec::functions::getApplicationId (from xml document or hexadecimal buffer).
61 * It is recommended to use Message class to create Avps (adding them through pair identification <code + vendor-id>
62 * prototype), but we could create Avps separately (other program section, i.e) and join them after:
67 * // Message creation:
68 * Message * msg = new Message(helpers::base::COMMANDID__Re_Auth_Answer, codecEngine);
69 * // Adding + creation:
70 * Avp * avp_sid = msg->addAvp(helpers::base::AVPID__Session_Id);
71 * Avp * avp_oh = msg->addAvp(helpers::base::AVPID__Origin_Host);
72 * Avp * avp_or = msg->addAvp(helpers::base::AVPID__Origin_Realm);
73 * Avp * avp_rc = msg->addAvp(helpers::base::AVPID__Result_Code);
75 * avp_sid->getUTF8String()->setValue("grump.example.com:33041;23432;893;0AF3B81");
78 * 2. External Avp creation:
80 * // Message creation:
81 * Message * msg = new Message(helpers::base::COMMANDID__Re_Auth_Answer, codecEngine);
83 * Avp * avp_sid = new Avp(helpers::base::AVPID__Session_Id, codecEngine);
84 * Avp * avp_oh = new Avp(helpers::base::AVPID__Origin_Host, codecEngine);
85 * Avp * avp_or = new Avp(helpers::base::AVPID__Origin_Realm, codecEngine);
86 * Avp * avp_rc = new Avp(helpers::base::AVPID__Result_Code, codecEngine);
88 * msg->addAvp(avp_sid);
89 * msg->addAvp(avp_oh);
90 * msg->addAvp(avp_or);
91 * msg->addAvp(avp_rc);
93 * avp_sid->getUTF8String()->setValue("grump.example.com:33041;23432;893;0AF3B81");
96 * The main difference is that Avps created through Message (or through grouped Avps) are internally allocated
97 * through engine which normally implements a recycler to optimize memory use.
101 * Implementation example:
105 * class MyEngine : public EngineImpl {
107 * MyEngine (const char *className = "MyEngine") : Engine(className) {;}
110 * anna::Recycler<MyAvp> a_avps;
111 * anna::Recycler<MyMessage> a_messages;
113 * anna::diameter::codec::Avp* allocateAvp () throw () { return a_avps.create (); }
115 * void releaseAvp (anna::diameter::codec::Avp* avp) throw () {
116 * if (avp == NULL) return;
117 * MyAvp* aux = static_cast <MyAvp*> (avp);
118 * aux->clear(); // free internal data-part storage specially for grouped avps which will release its childrens
119 * a_avps.release (aux);
122 * anna::diameter::codec::Message* allocateMessage () throw () { return a_messages.create (); }
124 * void releaseMessage (anna::diameter::codec::Message* message) throw () {
125 * if (message == NULL) return;
126 * MyMessage* aux = static_cast <MyMessage*> (message);
127 * aux->clear(); // free internal data-part storage specially for childrens releasing
128 * a_messages.release (aux);
134 class EngineImpl : public anna::Component {
139 * Defines behaviour on validation procedure: complete analysis or stop at first validation error over the message (by default)
141 struct ValidationDepth { enum _v { Complete, FirstError /* default */ }; };
144 * Defines behaviour mode regarding when to validate a message: before encoding, after decoding (by default), always or never
145 * Anyway validation procedure may be called at any moment (#valid)
147 struct ValidationMode { enum _v { BeforeEncoding, AfterDecoding /* default */, Always, Never /* optimization */ }; };
150 * Defines behaviour mode regarding when to fix a message: before encoding (by default), after decoding, always or never.
151 * An application could add Avps in any order; the fix procedure try to adjust it regarding dictionary. I.e., fixed
152 * Avps will be placed on first position, before mandatory and optional ones, within the message level or any
153 * grouped Avp. Usually we need to configure fixing before encoding in order to provide flexibility to the application
154 * during message construction, but actually, optimal mode implies disabling this procedure. Fixing after decoding will
155 * hide any validation problem regarding Avps position at any level.
156 * Anyway fix procedure may be called at any moment (#fix)
158 struct FixMode { enum _v { BeforeEncoding /* default */, AfterDecoding, Always, Never /* optimization */ }; };
162 Avp* createAvp(const AvpId *id) throw(anna::RuntimeException);
163 Message* createMessage(const CommandId *id) throw(anna::RuntimeException);
168 ValidationDepth::_v a_validationDepth;
169 ValidationMode::_v a_validationMode;
170 bool a_singleFailedAVP;
172 FixMode::_v a_fixMode;
175 const stack::Dictionary * a_dictionary;
178 static const char* asText(const ValidationDepth::_v) throw();
179 static const char* asText(const ValidationMode::_v) throw();
180 static const char* asText(const FixMode::_v) throw();
185 @param className Logical name for the class.
186 @param dictionary Diameter dictionary. At single threaded processes, the same codec engine could be used with
187 different diameter dictionaries (multi-stack applications). In that case the process must switch the stack for
188 the whole decoding or enconding operation over a Message depending on the context (normally the message header
189 Application-Id is used as stack identifier). But the smart way implies inherit from this engine creating a
190 component for each diameter stack managed in the application. Inheritance is mandatory in multi-threaded processes:
191 one engine, a unique stack.
193 EngineImpl(const char* className, const stack::Dictionary * dictionary);
198 virtual ~EngineImpl() {;}
202 Gets currently configured dictionary. NULL if not configured (manual encode/decode operations).
204 @return Returns currently configured engine dictionary
206 const stack::Dictionary *getDictionary() const throw() { return a_dictionary; }
210 * Sets behaviour on validation procedure.
211 * \param validationDepth Behaviour on validation procedure: complete analysis or stop at first validation error over the message.
213 void setValidationDepth(const ValidationDepth::_v validationDepth) throw() { a_validationDepth = validationDepth; }
216 * Returns behaviour on on validation procedure.
217 * For practical purposes, the Failed-AVP would typically refer to the first AVP processing error that a Diameter node encounters
218 * (decoding error or validation error in this case).
219 * A complete validation depth incurs on multiple-content Failed-AVP and is perhaps useful during integration tasks.
221 * \return Behaviour on validation procedure: complete analysis or stop at first validation error over the message (by default).
223 ValidationDepth::_v getValidationDepth() const throw() { return a_validationDepth; }
226 * Sets behaviour on validation procedure regarding stack flags. Actually, only AVP flags M & P are affected. The vendor bit is
227 * too important to be ignored, and there is no way to check operation flags (as request bit).
228 * By default (at engine start), flags are verified.
229 * \param ignoreFlags Validation will ignore flags.
232 void ignoreFlagsOnValidation(bool ignoreFlags) throw() { a_ignoreFlags = ignoreFlags; }
235 * Gets behaviour on validation procedure regarding stack flags. Actually, only AVP flags M & P are affected. The vendor bit is
236 * too important to be ignored, and there is no way to check operation flags (as request bit).
237 * By default (at engine start), flags are verified.
238 * \return Validation ignore flags indicator.
240 bool ignoreFlagsOnValidation() const throw() { return a_ignoreFlags; }
243 * Sets validation mode.
244 * \param validationMode Validation mode: before encoding, after decoding, always or never.
246 void setValidationMode(const ValidationMode::_v validationMode) throw() { a_validationMode = validationMode; }
249 * Returns validation mode.
250 * Before coding validation mode is perhaps useful during debugging tasks, i.e. to check answer messages built by the application
251 * during development phase.
252 * \return Validation mode: before encoding, after decoding (by default), always or never.
254 ValidationMode::_v getValidationMode() const throw() { return a_validationMode; }
260 * \param fixMode Fix mode: before encoding, after decoding, always or never.
262 void setFixMode(const FixMode::_v fixMode) throw() { a_fixMode = fixMode; }
266 * \return Fix mode: before encoding (by default), after decoding, always or never.
268 FixMode::_v getFixMode() const throw() { return a_fixMode; }
271 * Sets single FailedAVP. True by default. If false, and more than one wrong avp are found during message
272 * decoding and or validation, a new Failed-AVP will be added to the dynamic answer provided. The standard
273 * talks about only one but it is open to do this.
275 * \param single Single Failed-AVP boolean.
277 void setSingleFailedAVP(bool single = true) throw() { a_singleFailedAVP = single; }
280 * Returns single Failed-AVP boolean.
281 * \return Failed-AVP could be one (true) or more (false) in answer message.
283 bool getSingleFailedAVP() const throw() { return a_singleFailedAVP; }
286 * Creates a new diameter avp assigning its identifier, using engine resources to allocate memory (recommended
287 * recycler allocation at engine component re-implementation of allocator methods). Obviously, normal objects
288 * creation (new) is possible.
290 * @param id Avp identifier. AVP flags will be established based on active dictionary for known avps, or
291 * uninitialized for unknown ones.
293 * @return Created avp ready to be used
295 Avp* createAvp(AvpId id) throw(anna::RuntimeException) { return createAvp(&id); }
298 * Creates a new diameter avp, using engine resources to allocate memory (recommended recycler allocation at
299 * engine component re-implementation of allocator methods). Obviously, normal objects creation (new) is possible.
301 * @return Created avp ready to be used
303 Avp* createAvp() throw(anna::RuntimeException) { return createAvp(NULL); }
306 * Creates a new diameter Message assigning its identifier, using engine resources to allocate memory (recommended
307 * recycler allocation at engine component re-implementation of allocator methods). Obviously, normal objects
308 * creation (new) is possible.
310 * @param id Command identifier. Message flags will be established based on active dictionary for known commands,
311 * or uninitialized for unknown ones.
313 * @return Created message ready to be used
315 Message* createMessage(CommandId id) throw(anna::RuntimeException) { return createMessage(&id); }
318 * Creates a new diameter message, using engine resources to allocate memory (recommended recycler allocation
319 * at engine component re-implementation of allocator methods). Obviously, normal objects creation (new) is possible.
321 * @return Created message ready to be used
323 Message* createMessage() throw(anna::RuntimeException) { return createMessage(NULL); }
327 Loads an xml file representing a diameter message base in a DTD document (#getDTD)
329 @param xmlPathFile Complete path file to the xml document which represents the diameter message
331 Message *createMessage(const std::string & xmlPathFile) throw(anna::RuntimeException);
335 Invoked to free Avps.
338 virtual void releaseAvp(Avp*) throw() = 0;
341 Invoked to free Messages.
344 virtual void releaseMessage(Message*) throw() = 0;
348 * Class string representation
350 * @return String with class content
352 virtual std::string asString(void) const throw();
355 Class XML representation.
356 \param parent XML node over which we will put instance information.
357 \return XML documentcon with class content.
359 virtual anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
363 Gets the Avp identifier providing its logical name at engine dictionary
365 @param name Name of the Avp at engine dictionary
367 @return Avp identifier as pair (code,vendor-id)
369 AvpId avpIdForName(const char * name) throw(anna::RuntimeException);
373 Gets the Command identifier providing its logical name at engine dictionary
375 @param name Name of the Command at engine dictionary
377 @return Command identifier as pair (code,request-indicator)
379 CommandId commandIdForName(const char * name) throw(anna::RuntimeException);
385 Avp allocator method.
387 It is recommended to use anna::Recycler for Avps creation/releasing.
391 virtual Avp* allocateAvp() throw() = 0;
395 Message allocator method.
397 It is recommended to use anna::Recycler for Message creation/releasing.
401 virtual Message* allocateMessage() throw() = 0;
405 Manages warning trace or exception on validation anomaly depending on ValidationDepth configuration
406 ('complete' and 'first error' reports respectively).
408 @description Anomaly description used in trace or exception
410 @see setValidationDepth
411 @see getValidationDepth
413 void validationAnomaly(const std::string & description) const throw(anna::RuntimeException);