Line data Source code
1 : // Copyright (c) 2012-2019 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // This program is free software; you can redistribute it and/or modify
4 : // it under the terms of the GNU General Public License as published by
5 : // the Free Software Foundation; either version 2 of the License, or
6 : // (at your option) any later version.
7 : //
8 : // This program is distributed in the hope that it will be useful,
9 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 : // GNU General Public License for more details.
12 : //
13 : // You should have received a copy of the GNU General Public License
14 : // along with this program; if not, write to the Free Software
15 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 :
17 : /** \file
18 : * \brief Implementation of the dispatcher_support class.
19 : *
20 : * The various connection classes that know how to handle a message derive
21 : * from this class. This means the message can then automatically get
22 : * dispatched.
23 : */
24 :
25 :
26 : // self
27 : //
28 : #include "eventdispatcher/dispatcher_support.h"
29 :
30 : #include "eventdispatcher/exception.h"
31 :
32 :
33 : // snaplogger lib
34 : //
35 : #include "snaplogger/message.h"
36 :
37 :
38 : // last include
39 : //
40 : #include <snapdev/poison.h>
41 :
42 :
43 :
44 : namespace ed
45 : {
46 :
47 :
48 :
49 : /** \brief The destuctor.
50 : *
51 : * The destructor cleans up the dispatcher support object.
52 : */
53 0 : dispatcher_support::~dispatcher_support()
54 : {
55 0 : }
56 :
57 :
58 : /** \brief Define a dispatcher to execute your functions.
59 : *
60 : * The dispatcher to use to dispatch messages when received. The dispatch
61 : * happens by matching the command name with the dispatcher_match and
62 : * calling the corresponding function.
63 : *
64 : * If no match is found, then nothing gets executed by the dispatcher and
65 : * your default process_message() function gets called instead. If you
66 : * use a "match all" type of entry in your dispatcher, then your
67 : * process_message() function never gets called.
68 : *
69 : * \param[in] d The pointer to your dispatcher object.
70 : */
71 0 : void dispatcher_support::set_dispatcher(dispatcher_base::pointer_t d)
72 : {
73 0 : f_dispatcher = d;
74 0 : }
75 :
76 :
77 : /** \brief Get the dispatcher used to execute your message functions.
78 : *
79 : * This function returns the dispatcher one set with the set_dispatcher()
80 : * function. It may be a nullptr.
81 : *
82 : * \warning
83 : * Note that it may return nullptr because the weak pointer was just
84 : * set to nullptr as the owner of the dispatcher was deleted.
85 : *
86 : * \return The pointer to the dispatcher used to execite messages or nullptr.
87 : */
88 0 : dispatcher_base::pointer_t dispatcher_support::get_dispatcher() const
89 : {
90 0 : return f_dispatcher.lock();
91 : }
92 :
93 :
94 : /** \brief Dispatcher the specified message.
95 : *
96 : * This dispatcher function searches for a function that matches the
97 : * command of the specified \p message.
98 : *
99 : * The dispatcher handles a vector of dispatcher_match structures each
100 : * of which defines a message that this daemon understands. The dispatch
101 : * is done on a match as determined the the f_match() static function.
102 : *
103 : * The function executes the f_execute() function on a match. If none of
104 : * the dispatcher_match entries match the input message, then the default
105 : * process resumes, which is to call the process_message() function. This
106 : * is done as a fallback and it should only be used if you want to be
107 : * able to handle very complex cases as in the snapcommunicator. In most
108 : * cases, having a function that handles your command(s) will be more
109 : * than enough.
110 : *
111 : * If you called the add_snap_communicator_commands() function on your
112 : * dispatcher, it won't be necessary to implement the process_message()
113 : * since it adds a last entry which is a "catch all" entry. This entry
114 : * uses the function that replies to the user with the UNKNOWN message.
115 : * Assuming you do not do anything extraordinary, you just need to
116 : * implement the ready() and stop() functions. If you have dynamic
117 : * commands that the default msg_help() wont' understand, then you
118 : * need to also implement the help() function.
119 : *
120 : * \param[in,out] message The message being dispatched.
121 : *
122 : * \return true if the dispatcher handled the message, false if the
123 : * process_message() function was called instead.
124 : */
125 0 : bool dispatcher_support::dispatch_message(message & msg)
126 : {
127 0 : auto d(f_dispatcher.lock());
128 0 : if(d != nullptr)
129 : {
130 : // we have a dispatcher installed, try to dispatch that message
131 : //
132 0 : if(d->dispatch(msg))
133 : {
134 0 : return true;
135 : }
136 : }
137 :
138 : // either there was no dispatcher installed or the message is
139 : // not in the list of messages handled by this dispatcher
140 : //
141 0 : process_message(msg);
142 :
143 0 : return false;
144 : }
145 :
146 :
147 : /** \brief A default implementation of the process_message() function.
148 : *
149 : * This function is adefault fallback for the process_message()
150 : * functionality. If you define a dispatcher, then you probably
151 : * won't need to define a process_message() which in most cases
152 : * would do the exact same thing but it would be called.
153 : *
154 : * This is especially true if you finish your list of matches
155 : * with the always_match() function and msg_reply_with_unknown()
156 : * as the function to run when that entry is hit.
157 : *
158 : * \code
159 : * {
160 : * nullptr
161 : * , &dispatcher<my_connection>::dispatcher_match::msg_reply_with_unknown
162 : * , &dispatcher<my_connection>::dispatcher_match::always_match
163 : * },
164 : * \endcode
165 : *
166 : * \todo
167 : * Look into fixing this function so it can send the UNKNOWN message itself.
168 : * That way we'd avoid the last entry in the match array, which would allow
169 : * us to have binary search (much faster).
170 : *
171 : * \param[in] message The message to be processed.
172 : */
173 0 : void dispatcher_support::process_message(message const & msg)
174 : {
175 : // We don't currently have access to the send_message() function from
176 : // here--the snap_inter_thread_message_connection class causes a problem
177 : // because it has two process_message() functions: process_message_a()
178 : // and process_message_b().
179 : //
180 : //snap::snap_communicator_message unknown;
181 : //unknown.reply_to(message);
182 : //unknown.set_command("UNKNOWN");
183 : //unknown.add_parameter("command", message.get_command());
184 : //if(!send_message(unknown, false))
185 : //{
186 : // SNAP_LOG_WARNING("could not reply with UNKNOWN message to \"")(message.get_command())("\"");
187 : //}
188 :
189 : SNAP_LOG_FATAL
190 0 : << "process_message() with message \""
191 0 : << msg.to_message()
192 0 : << "\" was not reimplemented in your class and"
193 0 : << " the always_match() was not used in your dispatcher matches.";
194 :
195 : throw event_dispatcher_implementation_error(
196 : "your class is not reimplementing the process_message()"
197 : " virtual function and your dispatcher did not catch mnessage \""
198 0 : + msg.to_message()
199 0 : + "\".");
200 : }
201 :
202 :
203 :
204 6 : } // namespace ed
205 : // vim: ts=4 sw=4 et
|