Changed LICENSE. Now referenced to web site and file on project root directory
[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 extern const char *MessageDTD;
40
41 class Message;
42 class Avp;
43
44
45 /**
46  * General component implementation of a diameter elements factory (messages, avps) and common resources which
47  *  configure encode/decode operations behaviour.
48  *
49  * Standard inheritance is done over codec::Engine, allocating basic Avp and Message classes.
50  * A child implementation could manage complex Avp classes with new data-part formats. Grouped ones could
51  *  allocate new complex Avps through such engine which knows how to allocate this special Avp's (also for
52  *  complex Message classes with application-specific setters and getters as credit-control related avps,
53  *  or some another context items). For example helpers for TME scope stands for a new engine component
54  *  called tme::Engine, allocating tme::Avp and tme::Message classes which support three new Avp formats:
55  *  ISDNNumber, ISDNAddress and Unsigned16. Anyway, main Message/Avp and Engine classes stand for all contexts
56  *  included in anna::diameter, that is to say, whole contexts (at the time only TME) will be included in future
57  *  when needed apart from the independent namespace version. Thank to this, single threaded applications could
58  *  use whole engine in a easy way.
59  *
60  * Usually an engine component is associated to a single diameter stack, although single threaded processes could
61  *  use a common engine alternating different stack dictionaries by mean #setDictionary, depending on which kind of
62  *  messages are being analyzed. Although the application must ensure that a single dictionary is activated during
63  *  the same context operations an Avp could be considered as Unknown if was created with another, and we could
64  *  have validation problems (i.e. if mandatory Avp bit is enabled). In general, managing Unknown data-part format
65  *  don't have to be a problem because it is interpreted as OctetString format. Depending on what setters/getters
66  *  we use, it could reach a RuntimeException at our application.
67  *
68  * At multithread processes we must use one heir engine per stack and never switching stacks within same component.
69  * We will use each engine for each context.
70  *
71  * It is recommended to use Message class to create Avps (adding them through pair identification <code + vendor-id>
72  *  prototype), but we could create Avps separately (other program section, i.e) and join them after:
73  *
74  * <pre>
75  * 1. Recommended way:
76  *
77  *    // Message creation:
78  *    Message * msg = new Message(helpers::base::COMMANDID__Re_Auth_Answer);
79  *    // Adding + creation:
80  *    Avp * avp_sid = msg->addAvp(helpers::base::AVPID__Session_Id);
81  *    Avp * avp_oh = msg->addAvp(helpers::base::AVPID__Origin_Host);
82  *    Avp * avp_or = msg->addAvp(helpers::base::AVPID__Origin_Realm);
83  *    Avp * avp_rc = msg->addAvp(helpers::base::AVPID__Result_Code);
84  *    // Setting data:
85  *    avp_sid->getUTF8String()->setValue("grump.example.com:33041;23432;893;0AF3B81");
86  *    ...
87  *
88  * 2. External Avp creation:
89  *
90  *    // Message creation:
91  *    Message * msg = new Message(helpers::base::COMMANDID__Re_Auth_Answer);
92  *    // Creation:
93  *    Avp * avp_sid = new Avp(helpers::base::AVPID__Session_Id);
94  *    Avp * avp_oh = new Avp(helpers::base::AVPID__Origin_Host);
95  *    Avp * avp_or = new Avp(helpers::base::AVPID__Origin_Realm);
96  *    Avp * avp_rc = new Avp(helpers::base::AVPID__Result_Code);
97  *    // Adding:
98  *    msg->addAvp(avp_sid);
99  *    msg->addAvp(avp_oh);
100  *    msg->addAvp(avp_or);
101  *    msg->addAvp(avp_rc);
102  *    // Setting data:
103  *    avp_sid->getUTF8String()->setValue("grump.example.com:33041;23432;893;0AF3B81");
104  *    ...
105  *
106  * The main difference is that Avps created through Message (or through grouped Avps) are internally allocated
107  *  through engine which normally implements a recycler to optimize memory use.
108  * </pre>
109  *
110  *
111  * Implementation example:
112  *
113  *  \code
114  *
115  *     class MyEngine : public EngineImpl {
116  *     public:
117  *        Engine (getClassName()) {;}
118  *        static const char* getClassName() throw() { return "<full-naming path>::MyEngine"; }
119  *
120  *     private:
121  *        anna::Recycler<MyAvp> a_avps;
122  *        anna::Recycler<MyMessage> a_messages;
123  *
124  *        anna::diameter::codec::Avp* allocateAvp () throw () { return a_avps.create (); }
125  *
126  *        void releaseAvp (anna::diameter::codec::Avp* avp) throw () {
127  *             if (avp == NULL) return;
128  *             MyAvp* aux = static_cast <MyAvp*> (avp);
129  *             aux->clear(); // free internal data-part storage specially for grouped avps which will release its childrens
130  *             a_avps.release (aux);
131  *        }
132  *
133  *        anna::diameter::codec::Message* allocateMessage () throw () { return a_messages.create (); }
134  *
135  *        void releaseMessage (anna::diameter::codec::Message* message) throw () {
136  *             if (message == NULL) return;
137  *             MyMessage* aux = static_cast <MyMessage*> (message);
138  *             aux->clear(); // free internal data-part storage specially for childrens releasing
139  *             a_messages.release (aux);
140  *        }
141  *     };
142  *
143  *  \endcode
144  */
145 class EngineImpl : public anna::Component {
146
147 public:
148
149   /**
150    * Defines behaviour on validation procedure: complete analysis or stop at first validation error over the message (by default)
151    */
152   struct ValidationDepth { enum _v { Complete, FirstError /* default */ }; };
153
154   /**
155    * Defines behaviour mode regarding when to validate a message: before encoding, after decoding (by default), always or never
156    * Anyway validation procedure may be called at any moment (#valid)
157    */
158   struct ValidationMode { enum _v { BeforeEncoding, AfterDecoding /* default */, Always, Never /* optimization */ }; };
159
160   /**
161    * Defines behaviour mode regarding when to fix a message: before encoding (by default), after decoding, always or never.
162    * An application could add Avps in any order; the fix procedure try to adjust it regarding dictionary. I.e., fixed
163    *  Avps will be placed on first position, before mandatory and optional ones, within the message level or any
164    *  grouped Avp. Usually we need to configure fixing before encoding in order to provide flexibility to the application
165    *  during message construction, but actually, optimal mode implies disabling this procedure. Fixing after decoding will
166    *  hide any validation problem regarding Avps position at any level.
167    * Anyway fix procedure may be called at any moment (#fix)
168    */
169   struct FixMode { enum _v { BeforeEncoding /* default */, AfterDecoding, Always, Never /* optimization */ }; };
170
171
172   // Creators
173   Avp* createAvp(const AvpId *id) throw(anna::RuntimeException);
174   Message* createMessage(const CommandId *id) throw(anna::RuntimeException);
175
176
177 private:
178
179   anna::xml::DTDMemory a_dtd;
180   ValidationDepth::_v a_validationDepth;
181   ValidationMode::_v a_validationMode;
182   bool a_ignoreFlags;
183   FixMode::_v a_fixMode;
184   bool a_selectStackWithApplicationId; // default behaviour: let the user switch the stack (false for this boolean)
185
186
187   // Auxiliary
188   const stack::Dictionary * a_dictionary;
189
190   // helpers
191   static const char* asText(const ValidationDepth::_v) throw();
192   static const char* asText(const ValidationMode::_v) throw();
193   static const char* asText(const FixMode::_v) throw();
194
195 public:
196
197   /** Constructor
198       @param className Logical name for the class.
199    */
200   EngineImpl(const char* className);
201
202   /**
203    * Destructor
204    */
205   virtual ~EngineImpl() {;}
206
207
208   // setters
209   /**
210      Sets diameter dictionary loaded at stack engine. It's recommended to configure a valid dictionary
211      (if not, or NULL provided at #setDictionary, all avps will be managed as 'Unknown' format and all
212      items will need to be manually updated, i.e. message and avp flags).
213
214      @param dictionary Diameter dictionary. At single threaded processes, the same codec engine could be used with
215      different diameter dictionaries (multi-stack applications). In that case the process must switch the stack for
216      the whole decoding or enconding operation over a Message depending on the context (normally the message header
217      Application-Id is used as stack identifier). But the smart way implies inherit from this engine creating a
218      component for each diameter stack managed in the application. Inheritance is mandatory in multi-threaded processes:
219      one engine, a unique stack.
220   */
221   void setDictionary(const stack::Dictionary * dictionary) throw() { a_dictionary = dictionary; }
222
223   /**
224   * Sets diameter dictionary loaded at stack engine with the provided identifier.
225   *
226   * @param stackId Stack identifier. When missing, NULL will be returned
227   * @return Returns configured dictionary (NULL if stack id was not found)
228   */
229   const stack::Dictionary *setDictionary(unsigned int stackId) throw();
230
231
232   /**
233   * By default, the user will select the appropiate stack id depending on the context (see #setDictionary), but
234   * some applications could consider interesting automatic stack selection based on managed messages (incoming
235   * decoded ones, or built messages to be encoded). By default, on engine construction, no changes are done.
236   * Multithreaded processes should have a unique codec engine for each managed stack (then you don't have to
237   * worry about), but mono processes with multistack implementation over the same-unique engine, should activate
238   * this to have the commonly recommended way to choose the stack: using the Application-Id value.
239   *
240   * @warning do not activate in case of multithreaded applications.
241   * @param enable Activates/deactivates the stack selection from the Application-Id value within the message header.
242   */
243   void selectStackWithApplicationId (bool enable) throw() { a_selectStackWithApplicationId = enable; }
244
245   // getters
246
247   /**
248     Gets the currently configured behaviour regarding stack selection for multistack codec engines in mono thread
249     applications.
250
251     @return True if selection is done with the Application-Id. False if no selection is performed (user responsibility).
252   */
253   bool hasSelectStackWithApplicationId (void) throw() { return a_selectStackWithApplicationId; }
254
255
256   /**
257      Gets currently configured dictionary. NULL if not configured (manual encode/decode operations).
258
259      @return Returns currently configured engine dictionary
260   */
261   const stack::Dictionary *getDictionary() const throw() { return a_dictionary; }
262
263
264   /**
265    * Sets behaviour on validation procedure.
266    * \param validationDepth Behaviour on validation procedure: complete analysis or stop at first validation error over the message.
267    */
268   void setValidationDepth(const ValidationDepth::_v validationDepth)  throw() { a_validationDepth = validationDepth; }
269
270   /**
271    * Returns behaviour on on validation procedure.
272    * For practical purposes, the Failed-AVP would typically refer to the first AVP processing error that a Diameter node encounters
273    * (decoding error or validation error in this case).
274    * A complete validation depth incurs on multiple-content Failed-AVP and is perhaps useful during integration tasks.
275    *
276    * \return Behaviour on validation procedure: complete analysis or stop at first validation error over the message (by default).
277    */
278   ValidationDepth::_v getValidationDepth() const throw() { return a_validationDepth; }
279
280   /**
281    * Sets behaviour on validation procedure regarding stack flags. Actually, only AVP flags M & P are affected. The vendor bit is
282    * too important to be ignored, and there is no way to check operation flags (as request bit).
283    * By default (at engine start), flags are verified.
284    * \param ignoreFlags Validation will ignore flags.
285    */
286
287   void ignoreFlagsOnValidation(bool ignoreFlags) throw() { a_ignoreFlags = ignoreFlags; }
288
289   /**
290    * Gets behaviour on validation procedure regarding stack flags. Actually, only AVP flags M & P are affected. The vendor bit is
291    * too important to be ignored, and there is no way to check operation flags (as request bit).
292    * By default (at engine start), flags are verified.
293    * \return Validation ignore flags indicator.
294    */
295   bool ignoreFlagsOnValidation() const throw() { return a_ignoreFlags; }
296
297   /**
298    * Sets validation mode.
299    * \param validationMode Validation mode: before encoding, after decoding, always or never.
300    */
301   void setValidationMode(const ValidationMode::_v validationMode)  throw() { a_validationMode = validationMode; }
302
303   /**
304    * Returns validation mode.
305    * Before coding validation mode is perhaps useful during debugging tasks, i.e. to check answer messages built by the application
306    * during development phase.
307    * \return Validation mode: before encoding, after decoding (by default), always or never.
308    */
309   ValidationMode::_v getValidationMode() const throw() { return a_validationMode; }
310
311
312
313   /**
314    * Sets fix mode.
315    * \param fixMode Fix mode: before encoding, after decoding, always or never.
316    */
317   void setFixMode(const FixMode::_v fixMode)  throw() { a_fixMode = fixMode; }
318
319   /**
320    * Returns fix mode.
321    * \return Fix mode: before encoding (by default), after decoding, always or never.
322    */
323   FixMode::_v getFixMode() const throw() { return a_fixMode; }
324
325
326
327   /**
328      DTD document for xml message parsing
329   */
330   const anna::xml::DTDMemory & getDTD() const throw() { return a_dtd; }
331
332   /**
333   * Creates a new diameter avp assigning its identifier, using engine resources to allocate memory (recommended
334   *  recycler allocation at engine component re-implementation of allocator methods). Obviously, normal objects
335   *  creation (new) is possible.
336   *
337   * @param id Avp identifier. AVP flags will be established based on active dictionary for known avps, or
338   * uninitialized for unknown ones.
339   *
340   * @return Created avp ready to be used
341   */
342   Avp* createAvp(AvpId id) throw(anna::RuntimeException) { return createAvp(&id); }
343
344   /**
345   * Creates a new diameter avp, using engine resources to allocate memory (recommended recycler allocation at
346   *  engine component re-implementation of allocator methods). Obviously, normal objects creation (new) is possible.
347   *
348   * @return Created avp ready to be used
349   */
350   Avp* createAvp() throw(anna::RuntimeException) { return createAvp(NULL); }
351
352   /**
353   * Creates a new diameter Message assigning its identifier, using engine resources to allocate memory (recommended
354   *  recycler allocation at engine component re-implementation of allocator methods). Obviously, normal objects
355   *  creation (new) is possible.
356   *
357   * @param id Command identifier. Message flags will be established based on active dictionary for known commands,
358   *  or uninitialized for unknown ones.
359   *
360   * @return Created message ready to be used
361   */
362   Message* createMessage(CommandId id) throw(anna::RuntimeException) { return createMessage(&id); }
363
364   /**
365   * Creates a new diameter message, using engine resources to allocate memory (recommended recycler allocation
366   *  at engine component re-implementation of allocator methods). Obviously, normal objects creation (new) is possible.
367   *
368   * @return Created message ready to be used
369   */
370   Message* createMessage() throw(anna::RuntimeException) { return createMessage(NULL); }
371
372
373   /**
374      Loads an xml file representing a diameter message base in a DTD document (#getDTD)
375
376      @param xmlPathFile Complete path file to the xml document which represents the diameter message
377   */
378   Message *createMessage(const std::string & xmlPathFile) throw(anna::RuntimeException);
379
380
381   /**
382      Invoked to free Avps.
383      \see anna::Recycler
384   */
385   virtual void releaseAvp(Avp*) throw() = 0;
386
387   /**
388      Invoked to free Messages.
389      \see anna::Recycler
390   */
391   virtual void releaseMessage(Message*) throw() = 0;
392
393
394   /**
395   * Class string representation
396   *
397   * @return String with class content
398   */
399   virtual std::string asString(void) const throw();
400
401   /**
402      Class XML representation.
403      \param parent XML node over which we will put instance information.
404      \return XML documentcon with class content.
405   */
406   virtual anna::xml::Node* asXML(anna::xml::Node* parent) const throw();
407
408
409   /**
410      Gets the Avp identifier providing its logical name at engine dictionary
411
412      @param name Name of the Avp at engine dictionary
413
414      @return Avp identifier as pair (code,vendor-id)
415   */
416   AvpId avpIdForName(const char * name) throw(anna::RuntimeException);
417
418
419   /**
420      Gets the Command identifier providing its logical name at engine dictionary
421
422      @param name Name of the Command at engine dictionary
423
424      @return Command identifier as pair (code,request-indicator)
425   */
426   CommandId commandIdForName(const char * name) throw(anna::RuntimeException);
427
428
429 protected:
430
431   /**
432      Avp allocator method.
433
434      It is recommended to use anna::Recycler for Avps creation/releasing.
435
436      \see anna::Recycler
437   */
438   virtual Avp* allocateAvp() throw() = 0;
439
440
441   /**
442      Message allocator method.
443
444      It is recommended to use anna::Recycler for Message creation/releasing.
445
446      \see anna::Recycler
447   */
448   virtual Message* allocateMessage() throw() = 0;
449
450
451   /**
452      Manages warning trace or exception on validation anomaly depending on ValidationDepth configuration
453      ('complete' and 'first error' reports respectively).
454
455      @description Anomaly description used in trace or exception
456
457      @see setValidationDepth
458      @see getValidationDepth
459   */
460   void validationAnomaly(const std::string & description) const throw(anna::RuntimeException);
461 };
462
463 }
464 }
465 }
466
467 #endif
468