0305727a4ed3a0dc3503fb87625297677a821ee1
[anna.git] / include / anna / diameter / codec / EngineImpl.hpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
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 //
7
8
9 #ifndef anna_diameter_codec_EngineImpl_hpp
10 #define anna_diameter_codec_EngineImpl_hpp
11
12
13 // STL
14 #include <string>
15
16 #include <anna/core/RuntimeException.hpp>
17 #include <anna/xml/DTDMemory.hpp>
18 #include <anna/core/util/Recycler.hpp>
19
20 #include <anna/core/util/Component.hpp>
21 #include <anna/diameter/defines.hpp>
22
23
24 //------------------------------------------------------------------------------
25 //---------------------------------------------------------------------- #define
26 //------------------------------------------------------------------------------
27
28
29 namespace anna {
30
31 namespace diameter {
32
33 namespace stack {
34 class Dictionary;
35 }
36
37 namespace codec {
38
39 class Message;
40 class Avp;
41
42
43 /**
44  * General component implementation of a diameter elements factory (messages, avps) and common resources which
45  *  configure encode/decode operations behaviour.
46  *
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.
56  *
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).
60  *
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:
63  *
64  * <pre>
65  * 1. Recommended way:
66  *
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);
74  *    // Setting data:
75  *    avp_sid->getUTF8String()->setValue("grump.example.com:33041;23432;893;0AF3B81");
76  *    ...
77  *
78  * 2. External Avp creation:
79  *
80  *    // Message creation:
81  *    Message * msg = new Message(helpers::base::COMMANDID__Re_Auth_Answer, codecEngine);
82  *    // Creation:
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);
87  *    // Adding:
88  *    msg->addAvp(avp_sid);
89  *    msg->addAvp(avp_oh);
90  *    msg->addAvp(avp_or);
91  *    msg->addAvp(avp_rc);
92  *    // Setting data:
93  *    avp_sid->getUTF8String()->setValue("grump.example.com:33041;23432;893;0AF3B81");
94  *    ...
95  *
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.
98  * </pre>
99  *
100  *
101  * Implementation example:
102  *
103  *  \code
104  *
105  *     class MyEngine : public EngineImpl {
106  *     public:
107  *        MyEngine (const char *className = "MyEngine") : Engine(className) {;}
108  *
109  *     private:
110  *        anna::Recycler<MyAvp> a_avps;
111  *        anna::Recycler<MyMessage> a_messages;
112  *
113  *        anna::diameter::codec::Avp* allocateAvp () throw () { return a_avps.create (); }
114  *
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);
120  *        }
121  *
122  *        anna::diameter::codec::Message* allocateMessage () throw () { return a_messages.create (); }
123  *
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);
129  *        }
130  *     };
131  *
132  *  \endcode
133  */
134 class EngineImpl : public anna::Component {
135
136 public:
137
138   /**
139    * Defines behaviour on validation procedure: complete analysis or stop at first validation error over the message (by default)
140    */
141   struct ValidationDepth { enum _v { Complete, FirstError /* default */ }; };
142
143   /**
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)
146    */
147   struct ValidationMode { enum _v { BeforeEncoding, AfterDecoding /* default */, Always, Never /* optimization */ }; };
148
149   /**
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)
157    */
158   struct FixMode { enum _v { BeforeEncoding /* default */, AfterDecoding, Always, Never /* optimization */ }; };
159
160
161   // Creators
162   Avp* createAvp(const AvpId *id) throw(anna::RuntimeException);
163   Message* createMessage(const CommandId *id) throw(anna::RuntimeException);
164
165
166 private:
167
168   ValidationDepth::_v a_validationDepth;
169   ValidationMode::_v a_validationMode;
170   bool a_singleFailedAVP;
171   bool a_ignoreFlags;
172   FixMode::_v a_fixMode;
173
174   // Auxiliary
175   const stack::Dictionary * a_dictionary;
176
177   // helpers
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();
181
182 public:
183
184   /** Constructor
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.
192    */
193   EngineImpl(const char* className, const stack::Dictionary * dictionary);
194
195   /**
196    * Destructor
197    */
198   virtual ~EngineImpl() {;}
199
200
201   /**
202      Gets currently configured dictionary. NULL if not configured (manual encode/decode operations).
203
204      @return Returns currently configured engine dictionary
205   */
206   const stack::Dictionary *getDictionary() const throw() { return a_dictionary; }
207
208
209   /**
210    * Sets behaviour on validation procedure.
211    * \param validationDepth Behaviour on validation procedure: complete analysis or stop at first validation error over the message.
212    */
213   void setValidationDepth(const ValidationDepth::_v validationDepth)  throw() { a_validationDepth = validationDepth; }
214
215   /**
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.
220    *
221    * \return Behaviour on validation procedure: complete analysis or stop at first validation error over the message (by default).
222    */
223   ValidationDepth::_v getValidationDepth() const throw() { return a_validationDepth; }
224
225   /**
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.
230    */
231
232   void ignoreFlagsOnValidation(bool ignoreFlags) throw() { a_ignoreFlags = ignoreFlags; }
233
234   /**
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.
239    */
240   bool ignoreFlagsOnValidation() const throw() { return a_ignoreFlags; }
241
242   /**
243    * Sets validation mode.
244    * \param validationMode Validation mode: before encoding, after decoding, always or never.
245    */
246   void setValidationMode(const ValidationMode::_v validationMode)  throw() { a_validationMode = validationMode; }
247
248   /**
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.
253    */
254   ValidationMode::_v getValidationMode() const throw() { return a_validationMode; }
255
256
257
258   /**
259    * Sets fix mode.
260    * \param fixMode Fix mode: before encoding, after decoding, always or never.
261    */
262   void setFixMode(const FixMode::_v fixMode)  throw() { a_fixMode = fixMode; }
263
264   /**
265    * Returns fix mode.
266    * \return Fix mode: before encoding (by default), after decoding, always or never.
267    */
268   FixMode::_v getFixMode() const throw() { return a_fixMode; }
269
270   /**
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.
274    *
275    * \param single Single Failed-AVP boolean.
276    */
277   void setSingleFailedAVP(bool single = true)  throw() { a_singleFailedAVP = single; }
278
279   /**
280    * Returns single Failed-AVP boolean.
281    * \return Failed-AVP could be one (true) or more (false) in answer message.
282    */
283   bool getSingleFailedAVP() const throw() { return a_singleFailedAVP; }
284
285   /**
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.
289   *
290   * @param id Avp identifier. AVP flags will be established based on active dictionary for known avps, or
291   * uninitialized for unknown ones.
292   *
293   * @return Created avp ready to be used
294   */
295   Avp* createAvp(AvpId id) throw(anna::RuntimeException) { return createAvp(&id); }
296
297   /**
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.
300   *
301   * @return Created avp ready to be used
302   */
303   Avp* createAvp() throw(anna::RuntimeException) { return createAvp(NULL); }
304
305   /**
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.
309   *
310   * @param id Command identifier. Message flags will be established based on active dictionary for known commands,
311   *  or uninitialized for unknown ones.
312   *
313   * @return Created message ready to be used
314   */
315   Message* createMessage(CommandId id) throw(anna::RuntimeException) { return createMessage(&id); }
316
317   /**
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.
320   *
321   * @return Created message ready to be used
322   */
323   Message* createMessage() throw(anna::RuntimeException) { return createMessage(NULL); }
324
325
326   /**
327      Loads an xml file representing a diameter message base in a DTD document (#getDTD)
328
329      @param xmlPathFile Complete path file to the xml document which represents the diameter message
330   */
331   Message *createMessage(const std::string & xmlPathFile) throw(anna::RuntimeException);
332
333
334   /**
335      Invoked to free Avps.
336      \see anna::Recycler
337   */
338   virtual void releaseAvp(Avp*) throw() = 0;
339
340   /**
341      Invoked to free Messages.
342      \see anna::Recycler
343   */
344   virtual void releaseMessage(Message*) throw() = 0;
345
346
347   /**
348   * Class string representation
349   *
350   * @return String with class content
351   */
352   virtual std::string asString(void) const throw();
353
354   /**
355      Class XML representation.
356      \param parent XML node over which we will put instance information.
357      \return XML documentcon with class content.
358   */
359   virtual anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
360
361
362   /**
363      Gets the Avp identifier providing its logical name at engine dictionary
364
365      @param name Name of the Avp at engine dictionary
366
367      @return Avp identifier as pair (code,vendor-id)
368   */
369   AvpId avpIdForName(const char * name) throw(anna::RuntimeException);
370
371
372   /**
373      Gets the Command identifier providing its logical name at engine dictionary
374
375      @param name Name of the Command at engine dictionary
376
377      @return Command identifier as pair (code,request-indicator)
378   */
379   CommandId commandIdForName(const char * name) throw(anna::RuntimeException);
380
381
382 protected:
383
384   /**
385      Avp allocator method.
386
387      It is recommended to use anna::Recycler for Avps creation/releasing.
388
389      \see anna::Recycler
390   */
391   virtual Avp* allocateAvp() throw() = 0;
392
393
394   /**
395      Message allocator method.
396
397      It is recommended to use anna::Recycler for Message creation/releasing.
398
399      \see anna::Recycler
400   */
401   virtual Message* allocateMessage() throw() = 0;
402
403
404   /**
405      Manages warning trace or exception on validation anomaly depending on ValidationDepth configuration
406      ('complete' and 'first error' reports respectively).
407
408      @description Anomaly description used in trace or exception
409
410      @see setValidationDepth
411      @see getValidationDepth
412   */
413   void validationAnomaly(const std::string & description) const throw(anna::RuntimeException);
414 };
415
416 }
417 }
418 }
419
420 #endif
421