26 #ifndef _MEMORYSUBSCRIPTIONMANAGER_H_ 27 #define _MEMORYSUBSCRIPTIONMANAGER_H_ 32 #include <forward_list> 53 class SubscriptionInfo
58 unsigned requestedAckTypes_)
59 : _handler(messageHandler_)
62 , _requestedAckTypes(requestedAckTypes_)
66 std::string options = _m.getOptions();
67 size_t replace = options.find(
"replace");
69 static const size_t replaceLen = 7;
70 if (replace != std::string::npos)
72 options.erase(replace, replaceLen);
73 _m.setOptions(options);
75 _paused = (options.find(
"pause") != std::string::npos);
82 _m.getSubscriptionId().clear();
87 int timeout_,
void* userData_)
91 _m.setUserId((
const char*)0, 0);
95 if (_paused && !_recent.empty())
97 _m.setBookmark(_recent);
105 _m.setAckTypeEnum(_requestedAckTypes);
108 client_.
send(_handler, _m, timeout_, generation_);
114 if (client_.
send(handler, _m, timeout_, generation_).empty())
116 amps_invoke_remove_route_function(userData_);
136 unsigned requestedAcks()
const 138 return _requestedAckTypes;
144 size_t subIdLen = subId_.
len();
145 const char* subIdData = subId_.
data();
146 while (subIdLen && *subIdData ==
',')
151 while (subIdLen && subIdData[subIdLen - 1] ==
',')
155 if (subIdLen == 0 || subIdLen > _subId.len())
157 return _subId.empty();
160 size_t matchStart = 0;
161 size_t matchCount = 0;
162 for (
size_t i = 0; i < _subId.len(); ++i)
164 if (_subId.data()[i] ==
',')
166 if (matchCount == subIdLen)
176 if (_subId.data()[i] == subIdData[matchCount])
187 if (match && matchCount == subIdLen)
189 size_t newLen = _subId.len() - matchCount;
192 while (matchStart + matchCount < _subId.len() &&
193 _subId.data()[matchStart + matchCount] ==
',')
198 char* buffer = (
char*)malloc(newLen);
206 memcpy(buffer, _subId.data(), matchStart);
209 if (matchStart + matchCount < _subId.len())
211 memcpy(buffer + matchStart,
212 _subId.data() + matchStart + matchCount,
213 _subId.len() - matchStart - matchCount);
217 _m.getSubscriptionId().clear();
223 _m.assignSubscriptionId(buffer, newLen);
224 _subId = _m.getSubscriptionId();
231 _m.getSubscriptionId().clear();
236 _m.getSubscriptionId().assign(NULL, 0);
238 _subId = _m.getSubscriptionId();
242 return _subId.empty();
256 std::string opts(Message::Options::Pause());
257 opts.append(_m.getOptions());
262 std::string getMostRecent(
Client& client_)
264 if (!_recent.empty())
268 std::map<amps_uint64_t, amps_uint64_t> publishers;
269 const char* start = _subId.data();
270 const char* end = _subId.data() + _subId.len();
273 const char* comma = (
const char*)memchr(start,
',',
274 (
size_t)(end - start));
287 const char* sidRecentStart = sidRecent.
data();
288 const char* sidRecentEnd = sidRecent.
data() + sidRecent.
len();
289 while (sidRecentStart < sidRecentEnd)
291 const char* sidRecentComma = (
const char*)
292 memchr(sidRecentStart,
',',
293 (
size_t)(sidRecentEnd - sidRecentStart));
297 sidRecentComma = sidRecentEnd;
299 if (sidRecentComma == sidRecentStart)
301 sidRecentStart = sidRecentComma + 1;
305 (
size_t)(sidRecentComma - sidRecentStart));
306 amps_uint64_t publisher = (amps_uint64_t)0;
307 amps_uint64_t seq = (amps_uint64_t)0;
308 Field::parseBookmark(bookmark, publisher, seq);
309 if (publishers.count(publisher) == 0
310 || publishers[publisher] > seq)
312 publishers[publisher] = seq;
315 sidRecentStart = sidRecentComma + 1;
321 std::ostringstream os;
322 for (std::map<amps_uint64_t, amps_uint64_t>::iterator i = publishers.begin();
323 i != publishers.end();
326 if (i->first == 0 && i->second == 0)
334 os << i->first <<
'|' << i->second <<
"|";
340 void setMostRecent(
const std::string& recent_)
350 unsigned _requestedAckTypes;
357 typedef std::map<SubscriptionInfo*, AMPSException> FailedResubscribeMap;
363 AMPS_ATOMIC_BASE_TYPE _generation;
366 FailedResubscribeMap* _failures;
370 , _pManager(pManager_)
374 _failures =
new FailedResubscribeMap();
377 Resubscriber(
const Resubscriber& rhs_)
378 : _client(rhs_._client)
379 , _pManager(rhs_._pManager)
380 , _generation(rhs_._generation)
381 , _timeout(rhs_._timeout)
382 , _failures(rhs_._failures)
384 Resubscriber& operator=(
const Resubscriber& rhs_)
386 _client = rhs_._client;
387 _pManager = rhs_._pManager;
388 _generation = rhs_._generation;
389 _timeout = rhs_._timeout;
390 _failures = rhs_._failures;
393 void setGenerationCount(
void)
401 void operator()(SubscriptionInfo* iter_)
407 void* data = amps_invoke_copy_route_function(iter_->messageHandler().userData());
410 iter_->resubscribe(_client, _generation, _timeout, data);
413 catch (
const DisconnectedException&)
421 _failures->insert(std::make_pair(iter_, ex));
430 Deleter(
bool clearSubId_ =
false)
431 : _clearSubId(clearSubId_)
433 void operator()(std::pair<Message::Field, SubscriptionInfo*> iter_)
441 amps_invoke_remove_route_function(iter_.second->messageHandler().userData());
445 void operator()(SubscriptionInfo* iter_)
451 virtual SubscriptionInfo* createSubscriptionInfo(
MessageHandler messageHandler_,
453 unsigned requestedAckTypes_)
455 return new SubscriptionInfo(messageHandler_, message_,
460 typedef std::map<Message::Field, SubscriptionInfo*, Message::Field::FieldHash> SubscriptionMap;
463 : _generationCount(0)
480 const Message& message_,
unsigned requestedAckTypes_)
485 Lock<Mutex> l(_lock);
486 while (_resubscribing != 0)
491 if (options.find(
"resume") != std::string::npos)
494 SubscriptionInfo* subInfo = createSubscriptionInfo(
MessageHandler(),
498 Field fullSubId = subInfo->subId();
499 const char* start = fullSubId.
data();
500 const char* end = fullSubId.
data() + fullSubId.
len();
503 const char* comma = (
const char*)memchr(start,
',',
504 (
size_t)(end - start));
516 (
size_t)(comma - start));
519 if (_resumed.find(sid) == _resumed.end())
529 _resumedSet.insert(subInfo);
536 else if (options.find(
"pause") != std::string::npos)
538 const char* start = subId.
data();
539 const char* end = subId.
data() + subId.
len();
543 const char* comma = (
const char*)memchr(start,
',',
544 (
size_t)(end - start));
556 (
size_t)(comma - start));
557 SubscriptionMap::iterator resume = _resumed.find(sid);
558 if (resume != _resumed.end())
560 SubscriptionInfo* subPtr = resume->second;
562 _resumed.erase(resume);
565 if (subPtr->removeSubId(sid))
567 _resumedSet.erase(subPtr);
573 SubscriptionMap::iterator item = _active.find(sid);
574 if (item != _active.end())
576 if (options.find(
"replace") != std::string::npos)
578 messageHandler = item->second->messageHandler();
584 item->second->pause();
590 Unlock<Mutex> u(_lock);
591 void* data = amps_invoke_copy_route_function(
592 messageHandler_.userData());
595 messageHandler =
MessageHandler(messageHandler_.function(), data);
600 SubscriptionInfo* s = createSubscriptionInfo(messageHandler, m,
603 _active[s->subId()] = s;
609 SubscriptionMap::iterator item = _active.find(subId);
610 if (item != _active.end())
612 messageHandler = item->second->messageHandler();
618 Unlock<Mutex> u(_lock);
619 void* data = amps_invoke_copy_route_function(
620 messageHandler_.userData());
623 messageHandler =
MessageHandler(messageHandler_.function(), data);
626 SubscriptionInfo* s = createSubscriptionInfo(messageHandler,
630 _active[s->subId()] = s;
633 return (AMPS_ATOMIC_BASE_TYPE)_generationCount;
641 Lock<Mutex> l(_lock);
642 SubscriptionMap::iterator item = _active.find(subId_);
643 if (item != _active.end())
645 SubscriptionInfo* subPtr = item->second;
647 while (_resubscribing != 0)
651 Unlock<Mutex> u(_lock);
652 amps_invoke_remove_route_function(subPtr->messageHandler().userData());
655 item = _resumed.find(subId_);
656 if (item != _resumed.end())
658 SubscriptionInfo* subPtr = item->second;
660 _resumed.erase(item);
663 if (subPtr->removeSubId(subId_))
665 _resumedSet.erase(subPtr);
666 while (_resubscribing != 0)
686 Lock<Mutex> l(_lock);
687 while (_resubscribing != 0)
694 AtomicFlagFlip resubFlip(&_resubscribing);
696 Unlock<Mutex> u(_lock);
697 std::for_each(_active.begin(), _active.end(), Deleter());
698 std::for_each(_resumedSet.begin(), _resumedSet.end(), Deleter());
699 std::for_each(_resumed.begin(), _resumed.end(), Deleter(
true));
711 std::forward_list<SubscriptionInfo*> subscriptions;
712 Resubscriber resubscriber(client_,
this);
715 Lock<Mutex> l(_lock);
716 AMPS_FETCH_ADD(&_generationCount, 1);
717 while (_resubscribing != 0)
723 AtomicFlagFlip resubFlip(&_resubscribing);
725 resubscriber.setGenerationCount();
726 subscriptions.assign(_resumedSet.begin(), _resumedSet.end());
727 for (SubscriptionMap::iterator iter = _active.begin();
728 iter != _active.end(); ++iter)
730 SubscriptionInfo* sub = iter->second;
733 SubscriptionMap::iterator resIter = _resumed.find(sub->subId());
736 if (resIter != _resumed.end())
738 sub->setMostRecent(resIter->second->getMostRecent(client_));
741 subscriptions.push_front(iter->second);
744 Unlock<Mutex> unlock(_lock);
745 bool throwExcept =
false;
749 std::for_each(subscriptions.begin(), subscriptions.end(),
752 catch (
const DisconnectedException& disEx_)
761 throw DisconnectedException(
"Disconnect occurred during resubscribe");
763 std::vector<SubscriptionInfo*> removals;
764 if (_failedResubscribeHandler)
768 for (
auto failedSub = resubscriber._failures->begin();
769 failedSub != resubscriber._failures->end(); ++failedSub)
771 SubscriptionInfo* pSubInfo = failedSub->first;
772 if (_failedResubscribeHandler->failure(pSubInfo->message(),
773 pSubInfo->messageHandler(),
774 pSubInfo->requestedAcks(),
777 removals.push_back(pSubInfo);
783 except = failedSub->second;
797 catch (
const std::exception& ex_)
814 else if (!throwExcept)
816 throwExcept = !resubscriber._failures->empty();
819 except = resubscriber._failures->begin()->second;
823 if (_failedResubscribeHandler && !removals.empty())
825 Lock<Mutex> relock(_lock);
826 for (std::vector<SubscriptionInfo*>::iterator pSubInfo = removals.begin();
827 pSubInfo != removals.end(); ++pSubInfo)
829 _active.erase((*pSubInfo)->subId());
830 _resumed.erase((*pSubInfo)->subId());
831 _resumedSet.erase(*pSubInfo);
840 delete resubscriber._failures;
841 resubscriber._failures = 0;
845 delete resubscriber._failures;
846 resubscriber._failures = 0;
849 catch (
const std::exception&)
851 delete resubscriber._failures;
852 resubscriber._failures = 0;
864 _resubscriptionTimeout = timeout_;
873 return _resubscriptionTimeout;
882 static int _defaultResubscriptionTimeout =
883 AMPS_SUBSCRIPTION_MANAGER_DEFAULT_TIMEOUT;
886 _defaultResubscriptionTimeout = timeout_;
888 return _defaultResubscriptionTimeout;
906 return (AMPS_ATOMIC_BASE_TYPE)_generationCount;
911 SubscriptionMap _active;
912 SubscriptionMap _resumed;
913 std::set<SubscriptionInfo*> _resumedSet;
915 AMPS_ATOMIC_TYPE _generationCount;
916 AMPS_ATOMIC_TYPE_8 _resubscribing;
917 int _resubscriptionTimeout;
922 #endif //_MEMORYSUBSCRIPTIONMANAGER_H_ Field getSubscriptionId() const
Retrieves the value of the SubscriptionId header of the Message as a Field which references the under...
Definition: Message.hpp:1469
Message deepCopy(void) const
Returns a deep copy of self.
Definition: Message.hpp:567
int getResubscriptionTimeout(void)
Gets the timeout used when trying to resubscribe after disconnect.
Definition: MemorySubscriptionManager.hpp:871
Message encapsulates a single message sent to or received from an AMPS server, and provides methods f...
Definition: Message.hpp:519
static int getDefaultResubscriptionTimeout(void)
Gets the default timeout used by new MemorySubscriptionManager objects when trying to resubscribe aft...
Definition: MemorySubscriptionManager.hpp:895
void send(const Message &message)
Sends a Message to the connected AMPS server, performing only minimal validation and bypassing client...
Definition: ampsplusplus.hpp:5586
void setResubscriptionTimeout(int timeout_)
Sets the timeout used when trying to resubscribe after disconnect.
Definition: MemorySubscriptionManager.hpp:860
void clear()
Deletes the data associated with this Field, should only be used on Fields that were created as deepC...
Definition: Field.hpp:266
Abstract base class to manage all subscriptions placed on a client so that they can be re-established...
Definition: ampsplusplus.hpp:1461
Success.
Definition: amps.h:221
Field getOptions() const
Retrieves the value of the Options header of the Message as a Field which references the underlying b...
Definition: Message.hpp:1358
const char * data() const
Returns the (non-null-terminated) data underlying this field.
Definition: Field.hpp:279
AMPS_ATOMIC_BASE_TYPE subscribe(MessageHandler messageHandler_, const Message &message_, unsigned requestedAckTypes_)
Save a subscription so it can be placed again if a disconnect occurs.
Definition: MemorySubscriptionManager.hpp:479
Client represents a connection to an AMPS server, but does not provide failover or reconnection behav...
Definition: ampsplusplus.hpp:5341
AMPS_ATOMIC_BASE_TYPE getGenerationCount() const
Gets the current generation count, which is incremented each time resubcribe is called.
Definition: MemorySubscriptionManager.hpp:904
Base class for all exceptions in AMPS.
Definition: AMPSException.hpp:40
void _clear()
Clear all subscriptions from the manager.
Definition: MemorySubscriptionManager.hpp:684
A memory error occurred.
Definition: amps.h:225
Message & setSubscriptionId(const std::string &v)
Sets the value of the SubscriptionId header for this Message.
Definition: Message.hpp:1469
void resubscribe(Client &client_)
Place all saved subscriptions on the provided Client.
Definition: MemorySubscriptionManager.hpp:709
bool empty() const
Returns 'true' if empty, 'false' otherwise.
Definition: Field.hpp:129
Defines the AMPS::Field class, which represents the value of a field in a message.
Core type, function, and class declarations for the AMPS C++ client.
size_t len() const
Returns the length of the data underlying this field.
Definition: Field.hpp:286
BookmarkStore getBookmarkStore()
Get the bookmark store being used by the client.
Definition: ampsplusplus.hpp:5724
void clear()
Clear all subscriptions from the manager.
Definition: MemorySubscriptionManager.hpp:677
Field represents the value of a single field in a Message.
Definition: Field.hpp:87
The operation has not succeeded, but ought to be retried.
Definition: amps.h:245
void unsubscribe(const Message::Field &subId_)
Remove the subscription from the manager.
Definition: MemorySubscriptionManager.hpp:639
void deepCopy(const Field &orig_)
Makes self a deep copy of the original field.
Definition: Field.hpp:219
A SubscriptionManager implementation that maintains subscriptions placed in memory so that they can b...
Definition: MemorySubscriptionManager.hpp:49
Definition: AMPSException.hpp:32
static int setDefaultResubscriptionTimeout(int timeout_)
Sets the default timeout used by new MemorySubscriptionManager objects when trying to resubscribe aft...
Definition: MemorySubscriptionManager.hpp:880
Field getBookmark() const
Retrieves the value of the Bookmark header of the Message as a Field which references the underlying ...
Definition: Message.hpp:1236
Message::Field getMostRecent(const Message::Field &subId_)
Returns the most recent bookmark from the log that ought to be used for (re-)subscriptions.
Definition: BookmarkStore.hpp:310