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