reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
ProtonThreadBase.cxx
Go to the documentation of this file.
1
2#include <chrono>
3#include <thread>
4
5#include "Logger.hxx"
6#include "Subsystem.hxx"
8
9#include <proton/default_container.hpp>
10#include <proton/delivery.hpp>
11#include <proton/message.hpp>
12#include <proton/messaging_handler.hpp>
13#include <proton/connection.hpp>
14#include <proton/connection_options.hpp>
15#include <proton/container.hpp>
16#include <proton/reconnect_options.hpp>
17#include <proton/tracker.hpp>
18#include <proton/source_options.hpp>
19#include <proton/work_queue.hpp>
20
21// Cajun JSON
22#include "cajun/json/reader.h"
23#include "cajun/json/writer.h"
24#include "cajun/json/elements.h"
25
26using proton::receiver_options;
27using proton::source_options;
28
29#define RESIPROCATE_SUBSYSTEM Subsystem::QPIDPROTON
30
31using namespace resip;
32using namespace std;
33
35 std::chrono::duration<long int> maximumAge,
36 std::chrono::duration<long int> retryDelay)
37 : mMaximumAge(maximumAge),
38 mRetryDelay(retryDelay),
39 mUrl(u),
40 mWorkQueue(nullptr),
41 mFifo(0, 0)
42{
43}
44
48
50 std::chrono::duration<long int> retryDelay)
51 : mRetryDelay(retryDelay),
52 mUrl(u),
53 mWorkQueue(nullptr),
54 mFifo(0, 0),
55 mPending(0)
56{
57 StackLog(<<"ProtonSenderBase::ProtonSenderBase : " << mUrl);
58}
59
63
64void
66{
67 if(mFifo.add(new Data(msg), TimeLimitFifo<Data>::IgnoreTimeDepth))
68 {
69 StackLog(<<"QpidProtonThread::sendMessage added a message to the FIFO");
70 }
71 else
72 {
73 ErrLog(<<"mFifo rejected the message");
74 }
75 if(mWorkQueue)
76 {
77 mWorkQueue->add(proton::internal::v11::make_work(&ProtonThreadBase::ProtonSenderBase::doSend, this));
78 }
79}
80
81void
83{
84 try {
85 StackLog(<<"checking for a message");
86 while(mFifo.messageAvailable() && mSender.credit() > 0)
87 {
88 StackLog(<<"doSend trying to send a message");
89 std::shared_ptr<Data> body(mFifo.getNext());
90 proton::message msg;
91 msg.body(body->c_str());
92 mSender.send(msg);
93 StackLog(<<"doSend: mPending = " << ++mPending);
94 }
95 if(mFifo.messageAvailable())
96 {
97 StackLog(<<"tick still has messages to send, but no credit remaining");
98 }
99 }
100 catch(const std::exception& e)
101 {
102 ErrLog(<<"failed to send a message: " << e.what());
103 return;
104 }
105}
106
107ProtonThreadBase::ProtonThreadBase(std::chrono::duration<long int> retryDelay)
108 : mRetryDelay(retryDelay),
109 mConnected(false)
110{
111}
112
116
117void
118ProtonThreadBase::openReceiver(std::shared_ptr<ProtonReceiverBase> rx)
119{
120 StackLog(<<"calling open_receiver");
121 if(mConnected)
122 {
123 rx->mReceiver = mConnection.open_receiver(rx->mUrl);
124 rx->mWorkQueue = &rx->mReceiver.work_queue();
125 }
126 else
127 {
128 proton::reconnect_options rec;
129 rec.delay(proton::duration(std::chrono::milliseconds(rx->mRetryDelay).count()));
130 proton::connection_options co;
131 co.reconnect(rec);
132 rx->mReceiver = mContainer->open_receiver(rx->mUrl, co);
133 }
134}
135
136void
137ProtonThreadBase::openSender(std::shared_ptr<ProtonSenderBase> tx)
138{
139 StackLog(<<"calling open_sender for " << tx->mUrl);
140 if(mConnected)
141 {
142 tx->mSender = mConnection.open_sender(tx->mUrl);
143 tx->mWorkQueue = &tx->mSender.work_queue();
144 tx->mWorkQueue->add(proton::internal::v11::make_work(&ProtonThreadBase::ProtonSenderBase::doSend, tx));
145 }
146 else
147 {
148 proton::reconnect_options rec;
149 rec.delay(proton::duration(std::chrono::milliseconds(tx->mRetryDelay).count()));
150 proton::connection_options co;
151 co.reconnect(rec);
152 tx->mSender = mContainer->open_sender(tx->mUrl, co);
153 }
154}
155
156void
157ProtonThreadBase::addReceiver(std::shared_ptr<ProtonReceiverBase> rx)
158{
159 mReceivers.push_back(rx);
160 if(mStarted)
161 {
162 openReceiver(rx);
163 }
164}
165
166void
167ProtonThreadBase::addSender(std::shared_ptr<ProtonSenderBase> tx)
168{
169 mSenders.push_back(tx);
170 if(mStarted)
171 {
172 openSender(tx);
173 }
174}
175
176std::shared_ptr<ProtonThreadBase::ProtonSenderBase>
177ProtonThreadBase::getSenderForReply(const std::string& replyTo)
178{
179 std::shared_ptr<ProtonSenderBase> sender = std::make_shared<ProtonThreadBase::ProtonSenderBase>(replyTo);
180 addSender(sender);
181 return sender;
182}
183
184void
186{
187 mStarted = true;
188 for(auto rb : mReceivers)
189 {
190 openReceiver(rb);
191 }
192 for(auto sb : mSenders)
193 {
194 openSender(sb);
195 }
196}
197
198void
200{
201 InfoLog(<<"ProtonThreadBase::on_connection_open");
202 mConnection = conn;
203 mConnected = true;
204}
205
206void
208{
209 for(auto rb : mReceivers)
210 {
211 if(rb->mReceiver == r)
212 {
213 InfoLog(<<"receiver ready for queue " << rb->mUrl);
214 rb->mWorkQueue = &rb->mReceiver.work_queue();
215 return;
216 }
217 }
218 ErrLog(<<"unexpected receiver: " << r);
219}
220
221void
223{
224 InfoLog(<<"receiver closed");
225}
226
227void
229{
230 for(auto sb : mSenders)
231 {
232 if(sb->mSender == s)
233 {
234 InfoLog(<<"sender ready for queue " << sb->mUrl);
235 sb->mWorkQueue = &s.work_queue();
236 sb->doSend();
237 return;
238 }
239 }
240 ErrLog(<<"unexpected sender: " << s);
241}
242
243void
245{
246 DebugLog(<<"sender closed");
247}
248
249void
251{
252 WarningLog(<<"transport closed unexpectedly, reason: " << t.error());
253}
254
255void
256ProtonThreadBase::on_message(proton::delivery &d, proton::message &m)
257{
258 const proton::receiver& r = d.receiver();
259 for(auto rb : mReceivers)
260 {
261 if(rb->mReceiver == r)
262 {
263 const proton::timestamp::numeric_type& ct = m.creation_time().milliseconds();
264 StackLog(<<"message creation time (ms): " << ct);
265 if(ct > 0 && rb->mMaximumAge > (std::chrono::duration<long int>::zero()))
266 {
267 const proton::timestamp::numeric_type threshold = ResipClock::getTimeMs() - std::chrono::milliseconds(rb->mMaximumAge).count();
268 if(ct < threshold)
269 {
270 DebugLog(<<"dropping a message because it is too old: " << threshold - ct << "ms");
271 return;
272 }
273 }
274 rb->mFifo.add(new proton::message(m), TimeLimitFifo<proton::message>::InternalElement);
275
276 return;
277 }
278 }
279 ErrLog(<<"unexpected receiver: " << r);
280}
281
282std::unique_ptr<json::Object>
284{
285 std::unique_ptr<json::Object> result;
286 // get body as std::stringstream
287 std::string _json;
288 try
289 {
290 _json = proton::get<std::string>(m.body());
291 }
292 catch(proton::conversion_error& ex)
293 {
294 ErrLog(<<"failed to extract message body as string: " << ex.what());
295 return result;
296 }
297 StackLog(<<"on_message received: " << _json);
298 std::stringstream stream;
299 stream << _json;
300
301 // extract elements from JSON
302 json::Object* __json = new json::Object();
303 if(!__json)
304 {
305 ErrLog(<<"failed to allocate new json::Object()");
306 return result;
307 }
308 result.reset(__json);
309 try
310 {
311 json::Reader::Read(*result, stream);
312 }
313 catch(json::Reader::ScanException& ex)
314 {
315 ErrLog(<<"failed to scan JSON message: " << ex.what() << " message body: " << _json);
316 result.reset();
317 return result;
318 }
319 return result;
320}
321
322void
324{
325 StackLog(<<"on_sendable invoked");
326 for(auto sb : mSenders)
327 {
328 if(sb->mSender == s)
329 {
330 sb->doSend();
331 }
332 }
333}
334
335bool
336ProtonThreadBase::checkSenderShutdown()
337{
338 bool nothingOutstanding = true;
339 for(auto sb : mSenders)
340 {
341 if(sb->mFifo.messageAvailable() || sb->mPending > 0)
342 {
343 nothingOutstanding = false;
344 }
345 }
346
347 if(isShutdown() && nothingOutstanding)
348 {
349 StackLog(<<"no more messages outstanding, shutting down");
350 return true;
351 }
352 return false;
353}
354
355void
357{
358 for(auto sb : mSenders)
359 {
360 if(sb->mSender == t.sender())
361 {
362 StackLog(<<"on_tracker_accept: mPending = " << --sb->mPending);
363 }
364 }
365
366 if(checkSenderShutdown())
367 {
368 StackLog(<<"no more messages outstanding, shutting down");
369 doShutdown();
370 }
371}
372
373void
375{
376 while(!isShutdown())
377 {
378 try
379 {
380 mContainer.reset(new proton::container(*this));
381 mContainer->run();
382 }
383 catch(exception& e)
384 {
385 ErrLog(<<"ProtonThreadBase::thread container threw " << e.what());
386 }
387 if(!isShutdown())
388 {
389 WarningLog(<<"Proton container stopped unexpectedly, sleeping for " << std::chrono::milliseconds(mRetryDelay).count() << "ms before attempting to restart container");
390 std::this_thread::sleep_for(mRetryDelay);
391 }
392 }
393 mContainer.reset();
394 InfoLog(<<"ProtonThreadBase::thread container stopped");
395}
396
397void
399{
400 if(isShutdown())
401 {
402 DebugLog(<<"shutdown already in progress!");
403 return;
404 }
405 DebugLog(<<"trying to shutdown the Qpid Proton container");
407 if(!checkSenderShutdown())
408 {
409 return;
410 }
411 if(!mReceivers.empty())
412 {
413 if(mReceivers.front()->mWorkQueue)
414 {
415 mReceivers.front()->mWorkQueue->add(make_work(&ProtonThreadBase::doShutdown, this));
416 }
417 return;
418 }
419}
420
421void
422ProtonThreadBase::doShutdown()
423{
424 StackLog(<<"closing container");
425
426 if(mContainer)
427 {
428 mContainer->stop();
429 }
430}
431
432/* ====================================================================
433 *
434 * Copyright (C) 2022 Daniel Pocock https://danielpocock.com
435 * Copyright (C) 2022 Software Freedom Institute LLC https://softwarefreedom.institute
436 *
437 * Redistribution and use in source and binary forms, with or without
438 * modification, are permitted provided that the following conditions
439 * are met:
440 *
441 * 1. Redistributions of source code must retain the above copyright
442 * notice, this list of conditions and the following disclaimer.
443 *
444 * 2. Redistributions in binary form must reproduce the above copyright
445 * notice, this list of conditions and the following disclaimer in
446 * the documentation and/or other materials provided with the
447 * distribution.
448 *
449 * 3. Neither the name of the author(s) nor the names of any contributors
450 * may be used to endorse or promote products derived from this software
451 * without specific prior written permission.
452 *
453 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS "AS IS" AND
454 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
455 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
456 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE
457 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
458 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
459 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
460 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
461 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
462 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
463 * SUCH DAMAGE.
464 *
465 * ====================================================================
466 *
467 *
468 */
#define DebugLog(args_)
Definition Logger.hxx:128
#define StackLog(args_)
Definition Logger.hxx:125
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
std::unique_ptr< json::Object > getBodyAsJSON(const proton::message &m)
ProtonReceiverBase(const std::string &u, std::chrono::duration< long int > maximumAge, std::chrono::duration< long int > retryDelay)
ProtonSenderBase(const std::string &u, std::chrono::duration< long int > retryDelay=std::chrono::seconds(2))
void on_transport_error(proton::transport &t) override
void on_sender_close(proton::sender &) override
std::shared_ptr< ProtonSenderBase > getSenderForReply(const std::string &replyTo)
void on_sender_open(proton::sender &) override
void on_receiver_close(proton::receiver &) override
ProtonThreadBase(std::chrono::duration< long int > mRetryDelay=std::chrono::seconds(2))
void on_connection_open(proton::connection &conn) override
void addReceiver(std::shared_ptr< ProtonReceiverBase > rx)
void on_tracker_accept(proton::tracker &t) override
void on_container_start(proton::container &c) override
void on_receiver_open(proton::receiver &) override
void on_sendable(proton::sender &s) override
void addSender(std::shared_ptr< ProtonSenderBase > tx)
virtual void shutdown() override
virtual void thread() override
void on_message(proton::delivery &d, proton::message &m) override
static uint64_t getTimeMs()
Returns the current clock time in milliseconds.
Definition Time.hxx:69
virtual void shutdown()
Definition ThreadIf.cxx:190
bool isShutdown() const
Definition ThreadIf.cxx:212
dcm! – add UnusedChecking(_enum) below;