1 /* 2 * SObjectizer-5 3 */ 4 5 /*! 6 * \file 7 * \since 8 * v.5.5.23 9 * 10 * \brief Stuff related to enveloped messages. 11 */ 12 13 #pragma once 14 15 #include <so_5/message.hpp> 16 17 #include <so_5/optional.hpp> 18 19 namespace so_5 { 20 21 namespace enveloped_msg { 22 23 // 24 // payload_info_t 25 // 26 /*! 27 * \brief An information about payload inside envelope. 28 * 29 * This class is necessary for incapsulation of information related 30 * to payload inside an envelope. In v.5.5.23 this class contains only 31 * reference to the payload message. But additional info can be added 32 * in some future versions. 33 * 34 * \since 35 * v.5.5.23 36 */ 37 class payload_info_t 38 { 39 //! Actual enveloped message. 40 /*! 41 * This member is mutable because we need to return non-const 42 * reference to message_ref_t from const method message(). 43 * 44 * \note 45 * This pointer can be null if the enveloped message is a signal. 46 */ 47 mutable message_ref_t m_message; 48 49 public: 50 //! Initializing constructor. payload_info_t(message_ref_t message)51 payload_info_t( 52 message_ref_t message ) 53 : m_message{ std::move(message) } 54 {} 55 56 /*! 57 * \name Getters 58 * \{ 59 */ 60 [[nodiscard]] 61 message_ref_t & message() const62 message() const noexcept { return m_message; } 63 /*! 64 * \} 65 */ 66 }; 67 68 // 69 // handler_invoker_t 70 // 71 /*! 72 * \brief An extended version of handling_context which can be used 73 * for calling event handler. 74 * 75 * \note 76 * This class has non-virtual and protected destructor because 77 * creation on instances derived classes in dynamic memory 78 * is not intended. 79 * 80 * \since 81 * v.5.5.23 82 */ 83 class SO_5_TYPE handler_invoker_t 84 { 85 protected: 86 // clang requires this. 87 handler_invoker_t() = default; 88 handler_invoker_t( const handler_invoker_t & ) = default; 89 handler_invoker_t & operator=( const handler_invoker_t & ) = default; 90 91 handler_invoker_t( handler_invoker_t && ) = default; 92 handler_invoker_t & operator=( handler_invoker_t && ) = default; 93 94 ~handler_invoker_t() = default; 95 96 public: 97 //! Call an actual handler for the enveloped message/signal. 98 virtual void 99 invoke( const payload_info_t & payload ) noexcept = 0; 100 }; 101 102 // 103 // access_context_t 104 // 105 /*! 106 * \brief Information about context on that enveloped message is handled. 107 * 108 * \since 109 * v.5.5.23 110 */ 111 enum class access_context_t 112 { 113 //! Enveloped message is delivered to a receiver and the payload 114 //! is necessary for calling event handler. 115 handler_found, 116 //! The content of enveloped message should be transformed to 117 //! another representation. 118 //! For example it can be necessary for limit_then_transform 119 //! overload reaction. 120 transformation, 121 //! The content of enveloped message should be analyzed for 122 //! the further delivery. 123 //! For example it can be necessary for delivery filters. 124 inspection 125 }; 126 127 // 128 // envelope_t 129 // 130 /*! 131 * \brief An interface of envelope with some message/signal inside. 132 * 133 * SObjectizer v.5.5.23 introduced a new thing: enveloped messages. 134 * It means that actual message/signal is placed into a special 135 * container called 'envelope'. This envelope is delivered to all 136 * receivers of the original message/signal. But before the calling 137 * of event handler in a receiver the original message/signal (e.g. payload) 138 * are extracted from envelope and passed to the event handler. 139 * 140 * This interface describes 'envelope' for such containers. All envelopes 141 * should implement this interface. 142 * 143 * Method access_hook() is called by SObjectizer when the payload 144 * of enveloped message should be accessed. For example: 145 * 146 * - envelope is delivered to a receiver and receiver is ready to handle a 147 * message from envelope; 148 * - envelope can't be delivered to a receiver in its current form and there is 149 * a need to transform the message/signal from the envelope to another type 150 * of message/signal. For example it can happen when limit_then_transform is 151 * used for overload control. In that case the payload should be extracted 152 * and passed to a transformation function; 153 * - envelope should be analyzed by a delivery filter for further delivery 154 * of message. 155 * 156 * When access_hook() is called the envelope should check the 157 * availability of the payload and, if the payload is available for processing, 158 * should pass the payload info to handler_invoker_t::invoke() method. 159 * 160 * Please note that call of handler_invoker_t::invoke() is not guaranteed. 161 * Envelope can check some conditions (like payload expiration or revocation) 162 * and does call to invoke() only if these conditions are meet. But if some 163 * conditions are not fulfilled then access_hook() won't call 164 * handler_invoker_t::invoke() method. 165 * 166 * \since 167 * v.5.5.23 168 */ 169 class SO_5_TYPE envelope_t : public message_t 170 { 171 public: 172 // Introduce some names into the scope of this class to 173 // be easily used in derived classes outside so_5 namespace. 174 using payload_info_t = ::so_5::enveloped_msg::payload_info_t; 175 using handler_invoker_t = ::so_5::enveloped_msg::handler_invoker_t; 176 using access_context_t = ::so_5::enveloped_msg::access_context_t; 177 178 // clang requires this. 179 envelope_t() = default; 180 envelope_t( const envelope_t & ) = default; 181 envelope_t & operator=( const envelope_t & ) = default; 182 183 envelope_t( envelope_t && ) = default; 184 envelope_t & operator=( envelope_t && ) = default; 185 186 virtual ~envelope_t() override = default; 187 188 virtual void 189 access_hook( 190 //! Why this hook is called. 191 access_context_t context, 192 //! Proxy object which can call an actual event handler. 193 handler_invoker_t & invoker ) noexcept = 0; 194 195 private : 196 kind_t so5_message_kind() const197 so5_message_kind() const noexcept override 198 { 199 return kind_t::enveloped_msg; 200 } 201 }; 202 203 // 204 // extract_payload_for_message_transformation 205 // 206 /*! 207 * \brief Helper function for extraction of a payload 208 * from enveloped message. 209 * 210 * Extraction of a payload from an envelope is not an easy task. 211 * It is necessary to create an implementation of handler_invoker_t 212 * interface and pass it to envelope_t::transformation_hook() method. 213 * This implementation should check type of the payload: if it is 214 * another envelope then next call to transformation_hook() should 215 * be done and so on. 216 * 217 * Because of that extraction of the payload from an envelope is a boring 218 * task. This helper function preforms all described actions an returns 219 * optional with payload inside (if the payload available). 220 * 221 * \attention 222 * Argument \a envelope should not be nullptr. 223 * 224 * \since 225 * v.5.5.23 226 */ 227 [[nodiscard]] 228 SO_5_FUNC 229 optional< payload_info_t > 230 extract_payload_for_message_transformation( 231 //! Envelope with message inside. 232 const message_ref_t & envelope ); 233 234 // 235 // message_to_be_inspected 236 // 237 /*! 238 * \brief Helper function for extraction of a payload from enveloped 239 * message. 240 * 241 * This function checks the kind of \a msg_or_envelope. If this is 242 * an enveloped message message_to_be_inspected() will try to extract 243 * the payload and return it. In that case an empty \a optional object 244 * can be returned. 245 * 246 * If \a msg_or_envelope is not an envelope then \a msg_or_envelope 247 * is returned as a result. 248 * 249 * \since 250 * v.5.5.23 251 */ 252 [[nodiscard]] 253 SO_5_FUNC 254 optional< message_ref_t > 255 message_to_be_inspected( 256 const message_ref_t & msg_or_envelope ); 257 258 } /* namespace enveloped_msg */ 259 260 } /* namespace so_5 */ 261 262