LCOV - code coverage report
Current view: top level - eventdispatcher - dispatcher.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 25 105 23.8 %
Date: 2022-06-18 10:10:36 Functions: 28 93 30.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2012-2022  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/eventdispatcher
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License along
      17             : // with this program; if not, write to the Free Software Foundation, Inc.,
      18             : // 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19             : #pragma once
      20             : 
      21             : /** \file
      22             :  * \brief Event dispatch class.
      23             :  *
      24             :  * Class used to handle events.
      25             :  */
      26             : 
      27             : 
      28             : // self
      29             : //
      30             : #include    "eventdispatcher/dispatcher_base.h"
      31             : #include    "eventdispatcher/communicator.h"
      32             : #include    "eventdispatcher/utils.h"
      33             : 
      34             : 
      35             : // snaplogger
      36             : //
      37             : #include    <snaplogger/message.h>
      38             : 
      39             : 
      40             : 
      41             : namespace ed
      42             : {
      43             : 
      44             : 
      45             : 
      46             : 
      47             : /** \brief A template to create a list of messages to dispatch on receival.
      48             :  *
      49             :  * Whenever you receive messages, they can automatically get dispatched to
      50             :  * various functions using the dispatcher.
      51             :  *
      52             :  * You define a dispatcher_match array and then add a dispatcher to
      53             :  * your connection object.
      54             :  *
      55             :  * \code
      56             :  *      ed::dispatcher<my_connection>::dispatcher_match const my_messages[] =
      57             :  *      {
      58             :  *          ed::dispatcher<my_connection>::define_match(
      59             :  *              ed::dispatcher<my_connection>::Expression("HELP")
      60             :  *            , ed::dispatcher<my_connection>::Execute(&my_connection::msg_help)
      61             :  *            //, ed::dispatcher<my_connection>::MatchFunc(&ed::dispatcher<my_connection>::dispatcher_match::one_to_one_match) -- use default
      62             :  *          ),
      63             :  *          ed::dispatcher<my_connection>::define_match(
      64             :  *              ed::dispatcher<my_connection>::Expression("STATUS")
      65             :  *            , ed::dispatcher<my_connection>::Execute(&my_connection::msg_status)
      66             :  *            //, ed::dispatcher<my_connection>::MatchFunc(&dispatcher<my_connection>::dispatcher_match::one_to_one_match) -- use default
      67             :  *          },
      68             :  *          ... // other messages
      69             :  *
      70             :  *          // if you'd like you can end your list with a catch all which
      71             :  *          // generate the UNKNOWN message with the following (not required)
      72             :  *          // if you have that entry, your own process_message() function
      73             :  *          // will not get called
      74             :  *          ed::dispatcher<my_connection>::define_catch_all()
      75             :  *      };
      76             :  * \endcode
      77             :  *
      78             :  * In most cases you do not need to specify the matching function. It will
      79             :  * use the default which is a one to one match. So in the example above,
      80             :  * for "HELP", only a message with the command set to "HELP" will match.
      81             :  * When a match is found, the corresponding function (msg_help() here)
      82             :  * gets called.
      83             :  *
      84             :  * Note that "functions" are actually offsets. You will get `this` defined
      85             :  * as expected when your function gets called. The one drawback is that
      86             :  * only a function of the connection you attach the dispatcher to can
      87             :  * be called from the dispatcher. This is because we want to have a
      88             :  * static table instead of a dynamic one created each time we start
      89             :  * a process (in a website server, many processes get started again and
      90             :  * again.)
      91             :  *
      92             :  * \note
      93             :  * The T parameter of this template is often referenced as a "connection"
      94             :  * because it is expected to be a connection. There is actually no such
      95             :  * constraint on that object. It just needs to understand the dispatcher
      96             :  * usage which is to call the dispatch() function whenever a message is
      97             :  * received. Also it needs to implement any f_execute() function as
      98             :  * defined in its dispatcher_match vector.
      99             :  *
     100             :  * \note
     101             :  * This is documented here because it is a template and we cannot do
     102             :  * that in the .cpp (at least older versions of doxygen could not.)
     103             :  *
     104             :  * \todo
     105             :  * Transform the dispatcher_match with classes so we can build the
     106             :  * array safely.
     107             :  *
     108             :  * \tparam T  The connection class to be used with this dispathcer.
     109             :  */
     110             : template<typename T>
     111           8 : class dispatcher
     112             :     : public dispatcher_base
     113             : {
     114             : public:
     115             :     /** \brief A smart pointer of the dispatcher.
     116             :      *
     117             :      * Although we expect the array of `dispatcher_match` to be
     118             :      * statically defined, the `dispatcher`, on the other hand,
     119             :      * is quite dynamic and needs to be allocated in a smart
     120             :      * pointer then added to your connection.
     121             :      */
     122             :     typedef std::shared_ptr<dispatcher> pointer_t;
     123             : 
     124             :     /** \brief This structure is used to define the list of supported messages.
     125             :      *
     126             :      * Whenever you create an array of messages, you use this structure.
     127             :      *
     128             :      * The structure takes a few parameters as follow:
     129             :      *
     130             :      * \li f_expr -- the "expression" to be matched to the command name
     131             :      *               for example "HELP".
     132             :      * \li f_execute -- the function offset to execute on a match.
     133             :      * \li f_match -- the function to check whether the expression is a match;
     134             :      *                it has a default of one_to_one_match() which means the
     135             :      *                f_expr string is viewed as a plain string defining the
     136             :      *                message name as is.
     137             :      *
     138             :      * The command name is called "f_expr" but some matching functions may
     139             :      * make use of the "f_expr" parameter as an expression such as a
     140             :      * regular expression. Such functions will be added here with time, you
     141             :      * may also have your own, of course. The match function is expected to
     142             :      * be a static or standalone function.
     143             :      */
     144             :     struct dispatcher_match
     145             :     {
     146             :         /** \brief Define a vector of dispatcher_match objects.
     147             :          *
     148             :          * This function includes an array of dispatcher_match objects.
     149             :          * Whenever you define dispatcher_match objects, you want to
     150             :          * use the C++11 syntax to create a vector.
     151             :          *
     152             :          * \attention
     153             :          * We are NOT using a match because the matching may make use
     154             :          * of complex functions that support things such as complex as
     155             :          * regular expressions. In other words, the name of the message
     156             :          * may not just be a simple string.
     157             :          */
     158             :         typedef std::vector<dispatcher_match>       vector_t;
     159             : 
     160             :         /** \brief The execution function.
     161             :          *
     162             :          * This type defines the execution function. We give it the message
     163             :          * on a match. If the command name is not a match, it is ignored.
     164             :          */
     165             :         typedef void (T::*execute_func_t)(message & msg);
     166             : 
     167             :         /** \brief The match function return types.
     168             :          *
     169             :          * Whenever a match function is called, it may return one of:
     170             :          *
     171             :          * \li MATCH_FALSE
     172             :          *
     173             :          * The function did not match anything. Ignore the corresponding
     174             :          * function.
     175             :          *
     176             :          * \li MATCH_TRUE
     177             :          *
     178             :          * This is a match, execute the function. We are done with this list
     179             :          * of matches.
     180             :          *
     181             :          * \li MATCH_CALLBACK
     182             :          *
     183             :          * The function is a callback, it gets called and the process
     184             :          * continues. Since the message parameter is read/write, it is
     185             :          * a way to tweak the message before other functions get it.
     186             :          */
     187             :         enum class match_t
     188             :         {
     189             :             MATCH_FALSE,
     190             :             MATCH_TRUE,
     191             :             MATCH_CALLBACK
     192             :         };
     193             : 
     194             :         /** \brief The match function.
     195             :          *
     196             :          * This type defines the match function. We give it the message
     197             :          * which has the command name, although specialized matching
     198             :          * function could test other parameters from the message such
     199             :          * as the origination of the message.
     200             :          */
     201             :         typedef match_t (*match_func_t)(std::string const & expr, message & msg);
     202             : 
     203             :         /** \brief The default matching function.
     204             :          *
     205             :          * This function checks the command one to one to the expression.
     206             :          * The word in the expression is compared as is to the command
     207             :          * name:
     208             :          *
     209             :          * \code
     210             :          *          return expr == msg.get_command();
     211             :          * \endcode
     212             :          *
     213             :          * We will add other matching functions with time
     214             :          * (start_with_match(), regex_match(), etc.)
     215             :          *
     216             :          * \note
     217             :          * It is permissible to use a match function to modify the
     218             :          * message in some way, however, it is not recommended.
     219             :          *
     220             :          * \param[in] expr  The expression to compare the command against.
     221             :          * \param[in] msg  The message to match against this expression.
     222             :          *
     223             :          * \return MATCH_TRUE if it is a match, MATCH_FALSE otherwise.
     224             :          */
     225           8 :         static match_t one_to_one_match(std::string const & expr, message & msg)
     226             :         {
     227           8 :             return expr == msg.get_command()
     228           8 :                             ? match_t::MATCH_TRUE
     229           8 :                             : match_t::MATCH_FALSE;
     230             :         }
     231             : 
     232             :         /** \brief Always returns MATCH_TRUE.
     233             :          *
     234             :          * This function always returns MATCH_TRUE. This is practical to
     235             :          * close your list of messages and return a specific message. In
     236             :          * most cases this is used to reply with the UNKNOWN message.
     237             :          *
     238             :          * \param[in] expr  The expression to compare the command against.
     239             :          * \param[in] msg  The message to match against this expression.
     240             :          *
     241             :          * \return Always returns MATCH_TRUE.
     242             :          */
     243           0 :         static match_t always_match(std::string const & expr, message & msg)
     244             :         {
     245           0 :             snapdev::NOT_USED(expr, msg);
     246           0 :             return match_t::MATCH_TRUE;
     247             :         }
     248             : 
     249             :         /** \brief Always returns MATCH_CALLBACK.
     250             :          *
     251             :          * This function always returns MATCH_CALLBACK. It is used
     252             :          * to call the f_execute function as a callback. The processing
     253             :          * continues after calling a callback function (i.e. the
     254             :          * execute() function returns false, meaning that the message
     255             :          * was not yet processed). This is useful if you want to execute
     256             :          * some code against many or all messages before actually
     257             :          * processing the messages individually.
     258             :          *
     259             :          * \param[in] expr  The expression is ignored.
     260             :          * \param[in] msg  The message is ignored.
     261             :          *
     262             :          * \return Always returns MATCH_CALLBACK.
     263             :          */
     264           0 :         static match_t callback_match(std::string const & expr, message & msg)
     265             :         {
     266           0 :             snapdev::NOT_USED(expr, msg);
     267           0 :             return match_t::MATCH_CALLBACK;
     268             :         }
     269             : 
     270             :         /** \brief The expression to compare against.
     271             :          *
     272             :          * The expression is most often going to be the exact command name
     273             :          * which will be matched with the one_to_one_match() function.
     274             :          *
     275             :          * For other match functions, this would be whatever type of
     276             :          * expression supported by those other functions.
     277             :          *
     278             :          * \note
     279             :          * Effective C++ doesn't like bare pointers, but there is no real
     280             :          * reason for us to waste time and memory by having an std::string
     281             :          * here. It's going to always be a constant pointer anyway.
     282             :          */
     283             :         char const *        f_expr = nullptr;
     284             : 
     285             :         /** \brief The execute function.
     286             :          *
     287             :          * This is an offset in your connection class. We do not allow
     288             :          * std::bind() because we do not want the array of messages to be
     289             :          * dynamic (that way it is created at compile time and loaded as
     290             :          * ready/prepared data on load).
     291             :          *
     292             :          * The functions called have `this` defined so you can access
     293             :          * your connection data and other functions. It requires the
     294             :          * `&` and the class name to define the pointer, like this:
     295             :          *
     296             :          * \code
     297             :          *      &MyClass::my_message_function
     298             :          * \endcode
     299             :          *
     300             :          * The execution is started by calling the execute() function.
     301             :          */
     302             :         execute_func_t      f_execute = nullptr;
     303             : 
     304             :         /** \brief The match function.
     305             :          *
     306             :          * The match function is used to know whether that command
     307             :          * dispatch function was found.
     308             :          *
     309             :          * By default this parameter is set to one_to_one_match().
     310             :          * This means the command has to be one to one equal to
     311             :          * the f_expr string.
     312             :          *
     313             :          * The matching is done in the match() function.
     314             :          */
     315             :         match_func_t        f_match = &::ed::dispatcher<T>::dispatcher_match::one_to_one_match;
     316             : 
     317             :         /** \brief Run the execution function if this is a match.
     318             :          *
     319             :          * First this function checks whether the command of the message
     320             :          * in \p msg matches this `dispatcher_match` expression. In
     321             :          * most cases the match function is going to be
     322             :          * one_on_one_match() which means it has to be exactly equal.
     323             :          *
     324             :          * If it is a match, this function runs your \p connection execution
     325             :          * function (i.e. the message gets dispatched) and then it returns
     326             :          * true.
     327             :          *
     328             :          * If the message is not a match, then the function returns false
     329             :          * and only the matching function was called. In this case the
     330             :          * \p connection does not get used.
     331             :          *
     332             :          * When this function returns true, you should not call the
     333             :          * process_message() function since that was already taken care
     334             :          * of. The process_message() function should only be called
     335             :          * if the message was not yet dispatched. When the list of
     336             :          * matches includes a catch all at the end, the process_message()
     337             :          * will never be called.
     338             :          *
     339             :          * \note
     340             :          * Note that the dispatch_match has two functions: one to match
     341             :          * the message against the dispatch_match and one to execute when
     342             :          * f_match() returns MATCH_TRUE or MATCH_CALLBACK. That way only
     343             :          * the matching functions get called. Note that on MATCH_CALLBACK
     344             :          * the function returns `false` (i.e. continue to loop through
     345             :          * the supported messages). The MATCH_CALLBACK feature is rarely
     346             :          * used.
     347             :          *
     348             :          * \param[in] connection  The connection attached to that
     349             :          *                        `dispatcher_match`.
     350             :          * \param[in] msg  The message that matched.
     351             :          *
     352             :          * \return true if the connection execute function was called.
     353             :          */
     354           8 :         bool execute(T * connection, ::ed::message & msg) const
     355             :         {
     356           8 :             match_t m(f_match(f_expr == nullptr ? std::string() : f_expr, msg));
     357           8 :             if(m == match_t::MATCH_TRUE
     358           2 :             || m == match_t::MATCH_CALLBACK)
     359             :             {
     360           6 :                 (connection->*f_execute)(msg);
     361           6 :                 if(m == match_t::MATCH_TRUE)
     362             :                 {
     363           6 :                     return true;
     364             :                 }
     365             :             }
     366             : 
     367           2 :             return false;
     368             :         }
     369             : 
     370             :         /** \brief Check whether f_match is one_to_one_match().
     371             :          *
     372             :          * This function checks whether the f_match function was defined
     373             :          * to one_to_one_match()--the default--and if so returns true.
     374             :          *
     375             :          * \return true if f_match is the one_to_one_match() function.
     376             :          */
     377           0 :         bool match_is_one_to_one_match() const
     378             :         {
     379           0 :             return f_match == &ed::dispatcher<T>::dispatcher_match::one_to_one_match;
     380             :         }
     381             : 
     382             :         /** \brief Check whether f_match is always_match().
     383             :          *
     384             :          * This function checks whether the f_match function was defined
     385             :          * to always_match() and if so returns true.
     386             :          *
     387             :          * \return true if f_match is the always_match() function.
     388             :          */
     389           0 :         bool match_is_always_match() const
     390             :         {
     391           0 :             return f_match == &ed::dispatcher<T>::dispatcher_match::always_match;
     392             :         }
     393             : 
     394             :         /** \brief Check whether f_match is callback_match().
     395             :          *
     396             :          * This function checks whether the f_match function was defined
     397             :          * to callback_match() and if so returns true.
     398             :          *
     399             :          * \return true if f_match is the callback_match() function.
     400             :          */
     401           0 :         bool match_is_callback_match() const
     402             :         {
     403           0 :             return f_match == &ed::dispatcher<T>::dispatcher_match::callback_match;
     404             :         }
     405             :     };
     406             : 
     407             :     template<typename V>
     408             :     class MatchValue
     409             :     {
     410             :     public:
     411             :         typedef V   value_t;
     412             : 
     413           0 :         MatchValue<V>(V const v)
     414           0 :             : f_value(v)
     415             :         {
     416           0 :         }
     417             : 
     418           0 :         value_t get() const
     419             :         {
     420           0 :             return f_value;
     421             :         }
     422             : 
     423             :     private:
     424             :         value_t     f_value;
     425             :     };
     426             : 
     427             :     class Expression
     428             :         : public MatchValue<char const *>
     429             :     {
     430             :     public:
     431           0 :         Expression()
     432           0 :             : MatchValue<char const *>(nullptr)
     433             :         {
     434           0 :         }
     435             : 
     436           0 :         Expression(char const * expr)
     437           0 :             : MatchValue<char const *>(expr == nullptr || *expr == '\0' ? nullptr : expr)
     438             :         {
     439           0 :         }
     440             :     };
     441             : 
     442             :     class Execute
     443             :         : public MatchValue<typename ed::dispatcher<T>::dispatcher_match::execute_func_t>
     444             :     {
     445             :     public:
     446           0 :         Execute()
     447           0 :             : MatchValue<typename ed::dispatcher<T>::dispatcher_match::execute_func_t>(nullptr)
     448             :         {
     449           0 :         }
     450             : 
     451           0 :         Execute(typename ed::dispatcher<T>::dispatcher_match::execute_func_t expr)
     452           0 :             : MatchValue<typename ed::dispatcher<T>::dispatcher_match::execute_func_t>(expr)
     453             :         {
     454           0 :         }
     455             :     };
     456             : 
     457             :     class MatchFunc
     458             :         : public MatchValue<typename ed::dispatcher<T>::dispatcher_match::match_func_t>
     459             :     {
     460             :     public:
     461           0 :         MatchFunc()
     462           0 :             : MatchValue<typename ed::dispatcher<T>::dispatcher_match::match_func_t>(&::ed::dispatcher<T>::dispatcher_match::one_to_one_match)
     463             :         {
     464           0 :         }
     465             : 
     466           0 :         MatchFunc(typename ed::dispatcher<T>::dispatcher_match::match_func_t match)
     467           0 :             : MatchValue<typename ed::dispatcher<T>::dispatcher_match::match_func_t>(match == nullptr ? &::ed::dispatcher<T>::dispatcher_match::one_to_one_match : match)
     468             :         {
     469           0 :         }
     470             :     };
     471             : 
     472             :     template<typename V, typename F, class ...ARGS>
     473             :     static
     474             :     typename std::enable_if<std::is_same<V, F>::value, typename V::value_t>::type
     475           0 :     find_match_value(F first, ARGS ...args)
     476             :     {
     477           0 :         snapdev::NOT_USED(args...);
     478           0 :         return first.get();
     479             :     }
     480             : 
     481             :     template<typename V, typename F, class ...ARGS>
     482             :     static
     483             :     typename std::enable_if<!std::is_same<V, F>::value, typename V::value_t>::type
     484           0 :     find_match_value(F first, ARGS ...args)
     485             :     {
     486           0 :         snapdev::NOT_USED(first);
     487           0 :         return find_match_value<V>(args...);
     488             :     }
     489             : 
     490             :     template<class ...ARGS>
     491             :     static
     492           0 :     ed::dispatcher<T>::dispatcher_match define_match(ARGS ...args)
     493             :     {
     494             :     #pragma GCC diagnostic push
     495             :     #pragma GCC diagnostic ignored "-Wpedantic"
     496           0 :         ed::dispatcher<T>::dispatcher_match match =
     497             :         {
     498           0 :             .f_expr =    find_match_value<Expression  >(args..., Expression()),
     499           0 :             .f_execute = find_match_value<Execute     >(args..., Execute()),
     500           0 :             .f_match =   find_match_value<MatchFunc   >(args..., MatchFunc()),
     501             :         };
     502             :     #pragma GCC diagnostic pop
     503             : 
     504           0 :         if(match.f_execute == nullptr)
     505             :         {
     506           0 :             throw std::logic_error("an execute function is required, it cannot be set to nullptr.");
     507             :         }
     508             : 
     509           0 :         if(match.f_match == &ed::dispatcher<T>::dispatcher_match::one_to_one_match
     510           0 :         && match.f_expr == nullptr)
     511             :         {
     512             :             // although it works (won't crash) a message command cannot be
     513             :             // the empty string so we forbid that in our tables
     514             :             //
     515           0 :             throw std::logic_error("an expression is required for the one_to_one_match().");
     516             :         }
     517             : 
     518           0 :         return match;
     519             :     }
     520             : 
     521             :     static
     522           0 :     ed::dispatcher<T>::dispatcher_match define_catch_all()
     523             :     {
     524           0 :         return define_match(
     525             :               Execute(&T::msg_reply_with_unknown)
     526           0 :             , MatchFunc(&ed::dispatcher<T>::dispatcher_match::always_match));
     527             :     }
     528             : 
     529             : private:
     530             :     /** \brief The connection pointer.
     531             :      *
     532             :      * This parameter is set by the constructor. It represents the
     533             :      * connection this dispatcher was added to (a form of parent of
     534             :      * this dispatcher object.)
     535             :      */
     536             :     T *                         f_connection = nullptr;
     537             : 
     538             :     /** \brief The array of possible matches.
     539             :      *
     540             :      * This is the vector of your messages with the corresponding
     541             :      * match and execute functions. This is used to go through
     542             :      * the matches and execute (dispatch) as required.
     543             :      */
     544             :     typename ed::dispatcher<T>::dispatcher_match::vector_t  f_matches = {};
     545             : 
     546             :     /** \brief Tell whether messages should be traced or not.
     547             :      *
     548             :      * Because your service may accept and send many messages a full
     549             :      * trace on all of them can really be resource intensive. By default
     550             :      * the system will not trace anything. By setting this parameter to
     551             :      * true (call set_trace() for that) you request the SNAP_LOG_TRACE()
     552             :      * to run on each message received by this dispatcher. This is done
     553             :      * on entry so whether the message is processed by the dispatcher
     554             :      * or your own send_message() function, it will trace that message.
     555             :      */
     556             :     bool                        f_trace = false;
     557             : 
     558             : public:
     559             : 
     560             :     /** \brief Initialize the dispatcher with your connection and messages.
     561             :      *
     562             :      * This function takes a pointer to your connection and an array
     563             :      * of matches.
     564             :      *
     565             :      * Whenever a message is received by one of your connections, the
     566             :      * dispatch() function gets called which checks the message against
     567             :      * each entry in this array of \p matches.
     568             :      *
     569             :      * \param[in] connection  The connection for which this dispatcher is
     570             :      *                        created.
     571             :      * \param[in] matches  The array of dispatch keywords and functions.
     572             :      */
     573           4 :     dispatcher<T>(T * connection, typename ed::dispatcher<T>::dispatcher_match::vector_t matches)
     574             :         : f_connection(connection)
     575           4 :         , f_matches(matches)
     576             :     {
     577           4 :     }
     578             : 
     579             :     // prevent copies
     580             :     dispatcher<T>(dispatcher<T> const &) = delete;
     581             :     dispatcher<T> & operator = (dispatcher<T> const &) = delete;
     582             : 
     583             :     /** \brief Add a default array of possible matches.
     584             :      *
     585             :      * In Snap! a certain number of messages are always exactly the same
     586             :      * and these can be implemented internally so each daemon doesn't have
     587             :      * to duplicate that work over and over again. These are there in part
     588             :      * because the snapcommunicator expects those messages there.
     589             :      *
     590             :      * IMPORTANT NOTE: If you add your own version in your dispatcher_match
     591             :      * vector, then these will be ignored since your version will match first
     592             :      * and the dispatcher uses the first function only.
     593             :      *
     594             :      * This array currently includes:
     595             :      *
     596             :      * \li ALIVE -- msg_alive() -- auto-reply with ABSOLUTELY
     597             :      * \li HELP -- msg_help() -- returns the list of all the messages
     598             :      * \li LEAK -- msg_leak() -- log memory usage
     599             :      * \li LOG_ROTATE -- msg_log_rotate() -- reconfigure() the logger
     600             :      * \li QUITTING -- msg_quitting() -- calls stop(true);
     601             :      * \li READY -- msg_ready() -- calls ready() -- snapcommunicator always
     602             :      *              sends that message so it has to be supported
     603             :      * \li RESTART -- msg_restart() -- calls restart() -- it is triggered
     604             :      *                when a restart is required (i.e. the library was
     605             :      *                upgraded, a configuration file was updated, etc.)
     606             :      * \li STOP -- msg_stop() -- calls stop(false);
     607             :      * \li UNKNOWN -- msg_log_unknown() -- in case we receive a message we
     608             :      *                don't understand
     609             :      * \li * -- msg_reply_with_unknown() -- the last entry will be a grab
     610             :      *          all pattern which returns the UNKNOWN message automatically
     611             :      *          for you
     612             :      *
     613             :      * The msg_...() functions must be declared in your class T. If you
     614             :      * use the system connection_with_send_message class then they're
     615             :      * already defined there.
     616             :      *
     617             :      * The HELP response is automatically built from the f_matches.f_expr
     618             :      * strings. However, if the function used to match the expression is
     619             :      * not one_to_one_match(), then that string doesn't get used.
     620             :      *
     621             :      * If any message can't be determine (i.e. the function is not the
     622             :      * one_to_one_match()) then the user help() function gets called and
     623             :      * we expect that function to add any dynamic message the daemon
     624             :      * understands.
     625             :      *
     626             :      * The LOG message reconfigures the logger if the is_configure() function
     627             :      * says it is configured. In any other circumstances, nothing happens.
     628             :      *
     629             :      * Note that the UNKNOWN message is understood and just logs the message
     630             :      * received. This allows us to see that WE sent a message that the receiver
     631             :      * (not us) does not understand and adjust our code accordingly (i.e. add
     632             :      * support for that message in that receiver or maybe fix the spelling.)
     633             :      */
     634           0 :     void add_communicator_commands()
     635             :     {
     636             :         // avoid more than one realloc()
     637             :         //
     638           0 :         f_matches.reserve(f_matches.size() + 10);
     639             : 
     640           0 :         f_matches.push_back(define_match(Expression("ALIVE"),      Execute(&T::msg_alive)));
     641           0 :         f_matches.push_back(define_match(Expression("HELP"),       Execute(&T::msg_help)));
     642           0 :         f_matches.push_back(define_match(Expression("LEAK"),       Execute(&T::msg_leak)));
     643           0 :         f_matches.push_back(define_match(Expression("LOG_ROTATE"), Execute(&T::msg_log_rotate)));
     644           0 :         f_matches.push_back(define_match(Expression("QUITTING"),   Execute(&T::msg_quitting)));
     645           0 :         f_matches.push_back(define_match(Expression("READY"),      Execute(&T::msg_ready)));
     646           0 :         f_matches.push_back(define_match(Expression("RESTART"),    Execute(&T::msg_restart)));
     647           0 :         f_matches.push_back(define_match(Expression("STOP"),       Execute(&T::msg_stop)));
     648           0 :         f_matches.push_back(define_match(Expression("UNKNOWN"),    Execute(&T::msg_log_unknown)));
     649             : 
     650             :         // always last
     651             :         //
     652           0 :         f_matches.push_back(define_catch_all());
     653           0 :     }
     654             : 
     655             :     typename ed::dispatcher<T>::dispatcher_match::vector_t const & get_matches() const
     656             :     {
     657             :         return f_matches;
     658             :     }
     659             : 
     660             :     /** \brief The dispatch function.
     661             :      *
     662             :      * This is the function your message system will call whenever
     663             :      * the system receives a message.
     664             :      *
     665             :      * The function returns true if the message was dispatched.
     666             :      * When that happen, the process_message() function of the
     667             :      * connection should not be called.
     668             :      *
     669             :      * You may not include a message in the array of `dispatcher_match`
     670             :      * if it is too complicated to match or too many variables are
     671             :      * necessary then you will probably want to use your
     672             :      * process_message().
     673             :      *
     674             :      * By adding a catch-all at the end of your list of matches, you
     675             :      * can easily have one function called for any message. By default
     676             :      * the dispatcher environment offers such a match function and
     677             :      * it also includes a function that sends the UNKNOWN message as
     678             :      * an immediate reply to a received message.
     679             :      *
     680             :      * \param[in] msg  The message to be dispatched.
     681             :      *
     682             :      * \return true if the message was dispatched, false otherwise.
     683             :      */
     684           6 :     virtual bool dispatch(::ed::message & msg) override
     685             :     {
     686           6 :         if(f_trace)
     687             :         {
     688           6 :             SNAP_LOG_TRACE
     689             :                 << "dispatch message \""
     690             :                 << msg.to_message()
     691             :                 << "\"."
     692             :                 << SNAP_LOG_SEND;
     693             :         }
     694             : 
     695             :         // go in order to execute matches
     696             :         //
     697             :         // remember that a dispatcher with just a set of well defined command
     698             :         // names is a special case (albeit frequent) and we can't process
     699             :         // using a map (a.k.a. fast binary search) as a consequence
     700             :         //
     701           8 :         for(auto const & m : f_matches)
     702             :         {
     703           8 :             if(m.execute(f_connection, msg))
     704             :             {
     705           6 :                 return true;
     706             :             }
     707             :         }
     708             : 
     709           0 :         return false;
     710             :     }
     711             : 
     712             :     /** \brief Set whether the dispatcher should trace your messages or not.
     713             :      *
     714             :      * By default, the f_trace flag is set to false. You can change it to
     715             :      * true while debugging. You should remember to turn it back off once
     716             :      * you make an official version of your service to avoid the possibly
     717             :      * huge overhead of sending all those log messages. One way to do so
     718             :      * is to place the code within \#ifdef/\#endif as in:
     719             :      *
     720             :      * \code
     721             :      *     #ifdef _DEBUG
     722             :      *         my_dispatcher->set_trace();
     723             :      *     #endif
     724             :      * \endcode
     725             :      *
     726             :      * \param[in] trace  Set to true to get SNAP_LOG_TRACE() of each message.
     727             :      */
     728           4 :     void set_trace(bool trace = true)
     729             :     {
     730           4 :         f_trace = trace;
     731           4 :     }
     732             : 
     733             :     /** \brief Retrieve the list of commands.
     734             :      *
     735             :      * This function transforms the vector of f_matches in a list of
     736             :      * commands in a string_list_t.
     737             :      *
     738             :      * \param[in,out] commands  The place where the list of commands is saved.
     739             :      *
     740             :      * \return false if the commands were all determined, true if some need
     741             :      *         help from the user of this dispatcher.
     742             :      */
     743           0 :     virtual bool get_commands(string_list_t & commands) override
     744             :     {
     745           0 :         bool need_user_help(false);
     746           0 :         for(auto const & m : f_matches)
     747             :         {
     748           0 :             if(m.f_expr == nullptr)
     749             :             {
     750           0 :                 if(!m.match_is_always_match()
     751           0 :                 && !m.match_is_callback_match())
     752             :                 {
     753             :                     // this is a "special case" where the user has
     754             :                     // a magical function which does not require an
     755             :                     // expression at all (i.e. "hard coded" in a
     756             :                     // function)
     757             :                     //
     758           0 :                     need_user_help = true;
     759             :                 }
     760             :                 //else -- always match is the last entry and that just
     761             :                 //        means we can return UNKNOWN on an unknown message
     762             :             }
     763           0 :             else if(m.match_is_one_to_one_match())
     764             :             {
     765             :                 // add the f_expr as is since it represents a command
     766             :                 // as is
     767             :                 //
     768           0 :                 commands.push_back(m.f_expr);
     769             :             }
     770             :             else
     771             :             {
     772             :                 // this is not a one to one match, so possibly a
     773             :                 // full regex or similar
     774             :                 //
     775           0 :                 need_user_help = true;
     776             :             }
     777             :         }
     778           0 :         return need_user_help;
     779             :     }
     780             : };
     781             : 
     782             : 
     783             : 
     784             : } // namespace ed
     785             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13