AMPS C/C++ Client Class Reference AMPS C/C++ Client Version 5.3.5.7

ampsplusplus.hpp
Go to the documentation of this file.
1 //
3 // Copyright (c) 2010-2026 60East Technologies Inc., All Rights Reserved.
4 //
5 // This computer software is owned by 60East Technologies Inc. and is
6 // protected by U.S. copyright laws and other laws and by international
7 // treaties. This computer software is furnished by 60East Technologies
8 // Inc. pursuant to a written license agreement and may be used, copied,
9 // transmitted, and stored only in accordance with the terms of such
10 // license agreement and with the inclusion of the above copyright notice.
11 // This computer software or any other copies thereof may not be provided
12 // or otherwise made available to any other person.
13 //
14 // U.S. Government Restricted Rights. This computer software: (a) was
15 // developed at private expense and is in all respects the proprietary
16 // information of 60East Technologies Inc.; (b) was not developed with
17 // government funds; (c) is a trade secret of 60East Technologies Inc.
18 // for all purposes of the Freedom of Information Act; and (d) is a
19 // commercial item and thus, pursuant to Section 12.212 of the Federal
20 // Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
21 // Government's use, duplication or disclosure of the computer software
22 // is subject to the restrictions set forth by 60East Technologies Inc..
23 //
25 #ifndef _AMPSPLUSPLUS_H_
26 #define _AMPSPLUSPLUS_H_
27 #include "amps/amps.h"
28 #include "amps/ampsver.h"
29 #include <string>
30 #include <map>
31 #include <sstream>
32 #include <iostream>
33 #include <memory>
34 #include <stdexcept>
35 #include <limits.h>
36 #include <list>
37 #include <memory>
38 #include <regex>
39 #include <set>
40 #include <deque>
41 #include <vector>
42 #include <assert.h>
43 #ifndef _WIN32
44 #include <inttypes.h>
45 #endif
46 #if defined(sun)
47 #include <sys/atomic.h>
48 #endif
49 #include "amps/BookmarkStore.hpp"
50 #include "amps/MessageRouter.hpp"
51 #include "amps/util.hpp"
52 #include "amps/ampscrc.hpp"
53 #if __cplusplus >= 201100L || _MSC_VER >= 1900
54 #include <atomic>
55 #endif
56
57 #ifndef AMPS_TESTING_SLOW_MESSAGE_STREAM
58 #define AMPS_TESTING_SLOW_MESSAGE_STREAM
59 #endif
60
65
66
73
84
85 // For StoreBuffer implementations
86 #define AMPS_MEMORYBUFFER_DEFAULT_BUFFERS 10
87 #define AMPS_MEMORYBUFFER_DEFAULT_LENGTH 40960
88 #define AMPS_SUBSCRIPTION_MANAGER_DEFAULT_TIMEOUT 0
89 #define AMPS_HACLIENT_TIMEOUT_DEFAULT 10000
90 #define AMPS_HACLIENT_RECONNECT_DEFAULT 200
91 #define AMPS_DEFAULT_COMMAND_TIMEOUT 5000
92 #define AMPS_DEFAULT_TOP_N -1
93 #define AMPS_DEFAULT_BATCH_SIZE 10
94 #define AMPS_NUMBER_BUFFER_LEN 20
95 #define AMPS_DEFAULT_QUEUE_ACK_TIMEOUT 1000
96
97 #if defined(_M_X64) || defined(__x86_64) || defined(_WIN64)
98 #define AMPS_X64 1
99 #endif
100
101 static thread_local AMPS::Message threadLocalMessage;
102
103 namespace AMPS
104 {
105
106 typedef std::map<std::string, std::string> ConnectionInfo;
107
108 template<class Type>
109 inline std::string asString(Type x_)
110 {
111 std::ostringstream os;
112 os << x_;
113 return os.str();
114 }
115
116 inline
117 size_t convertToCharArray(char* buf_, amps_uint64_t seqNo_)
118 {
119 size_t pos = AMPS_NUMBER_BUFFER_LEN;
120 for (int i = 0; i < AMPS_NUMBER_BUFFER_LEN; ++i)
121 {
122 if (seqNo_ > 0)
123 {
124 buf_[--pos] = (char)(seqNo_ % 10 + '0');
125 seqNo_ /= 10;
126 }
127 }
128 return pos;
129 }
130
131 #ifdef _WIN32
132 inline
133 size_t convertToCharArray(char* buf_, unsigned long seqNo_)
134 {
135 size_t pos = AMPS_NUMBER_BUFFER_LEN;
136 for (int i = 0; i < AMPS_NUMBER_BUFFER_LEN; ++i)
137 {
138 if (seqNo_ > 0)
139 {
140 buf_[--pos] = (char)(seqNo_ % 10 + '0');
141 seqNo_ /= 10;
142 }
143 }
144 return pos;
145 }
146 #endif
147
151 class Reason
152 {
153 public:
154 static const char* duplicate()
155 {
156 return "duplicate";
157 }
158 static const char* badFilter()
159 {
160 return "bad filter";
161 }
162 static const char* badRegexTopic()
163 {
164 return "bad regex topic";
165 }
166 static const char* subscriptionAlreadyExists()
167 {
168 return "subscription already exists";
169 }
170 static const char* nameInUse()
171 {
172 return "name in use";
173 }
174 static const char* authFailure()
175 {
176 return "auth failure";
177 }
178 static const char* notEntitled()
179 {
180 return "not entitled";
181 }
182 static const char* authDisabled()
183 {
184 return "authentication disabled";
185 }
186 static const char* subidInUse()
187 {
188 return "subid in use";
189 }
190 static const char* noTopic()
191 {
192 return "no topic";
193 }
194 };
195
204 class ExceptionListener
205 {
206 public:
207 virtual ~ExceptionListener() {;}
208 virtual void exceptionThrown(const std::exception&) const {;}
209 };
210
211 typedef ExceptionListener DefaultExceptionListener;
212
213
214 #define AMPS_CALL_EXCEPTION_WRAPPER(x) \
215 try\
216 {\
217 x;\
218 }\
219 catch (std::exception& stdEx_)\
220 {\
221 try\
222 {\
223 _exceptionListener->exceptionThrown(stdEx_);\
224 }\
225 catch(...)\
226 {\
227 ;\
228 }\
229 }
230 /*
231 * Note : we don't attempt to trap non std::exception exceptions
232 * here because doing so interferes with pthread_exit on some OSes.
233 catch (...)\
234 {\
235 try\
236 {\
237 _exceptionListener->exceptionThrown(AMPS::AMPSException(\
238 "An unhandled exception of unknown type was thrown by "\
239 "the registered handler.", AMPS_E_USAGE));\
240 }\
241 catch(...)\
242 {\
243 ;\
244 }\
245 }*/
246 #ifdef _WIN32
247 #define AMPS_CALL_EXCEPTION_WRAPPER_2(me,x) \
248 try\
249 {\
250 while(me->_connected)\
251 {\
252 try\
253 {\
254 x;\
255 break;\
256 }\
257 catch(MessageStreamFullException&)\
258 {\
259 try\
260 {\
261 me->checkAndSendHeartbeat(false);\
262 }\
263 catch (std::exception& stdEx_)\
264 {\
265 try\
266 {\
267 me->_exceptionListener->exceptionThrown(stdEx_);\
268 }\
269 catch(...)\
270 {\
271 ;\
272 }\
273 break;\
274 }\
275 }\
276 }\
277 }\
278 catch (std::exception& stdEx_)\
279 {\
280 try\
281 {\
282 me->_exceptionListener->exceptionThrown(stdEx_);\
283 }\
284 catch(...)\
285 {\
286 ;\
287 }\
288 }
289 /*
290 * Note : we don't attempt to trap non std::exception exceptions
291 * here because doing so interferes with pthread_exit on some OSes.
292 catch (...)\
293 {\
294 try\
295 {\
296 me->_exceptionListener->exceptionThrown(AMPS::AMPSException(\
297 "An unhandled exception of unknown type was thrown by "\
298 "the registered handler.", AMPS_E_USAGE));\
299 }\
300 catch(...)\
301 {\
302 ;\
303 }\
304 }*/
305
306 #define AMPS_CALL_EXCEPTION_WRAPPER_STREAM_FULL_2(me, x)\
307 while(me->_connected)\
308 {\
309 try\
310 {\
311 x;\
312 break;\
313 }\
314 catch(MessageStreamFullException&)\
315 {\
316 try\
317 {\
318 me->checkAndSendHeartbeat(false);\
319 }\
320 catch (std::exception& stdEx_)\
321 {\
322 try\
323 {\
324 me->_exceptionListener->exceptionThrown(stdEx_);\
325 }\
326 catch(...)\
327 {\
328 ;\
329 }\
330 break;\
331 }\
332 }\
333 }
334 #else
335 #define AMPS_CALL_EXCEPTION_WRAPPER_2(me,x) \
336 try\
337 {\
338 while(me->_connected)\
339 {\
340 try\
341 {\
342 x;\
343 break;\
344 }\
345 catch(MessageStreamFullException& msfEx_)\
346 {\
347 try\
348 {\
349 me->checkAndSendHeartbeat(false);\
350 }\
351 catch (std::exception& stdEx_)\
352 {\
353 try\
354 {\
355 me->_exceptionListener->exceptionThrown(stdEx_);\
356 }\
357 catch(...)\
358 {\
359 ;\
360 }\
361 break;\
362 }\
363 }\
364 }\
365 }\
366 catch (std::exception& stdEx_)\
367 {\
368 try\
369 {\
370 me->_exceptionListener->exceptionThrown(stdEx_);\
371 }\
372 catch(...)\
373 {\
374 ;\
375 }\
376 }
377 /*
378 * Note : we don't attempt to trap non std::exception exceptions
379 * here because doing so interferes with pthread_exit on some OSes.
380 catch (...)\
381 {\
382 try\
383 {\
384 me->_exceptionListener->exceptionThrown(AMPS::AMPSException(\
385 "An unhandled exception of unknown type was thrown by "\
386 "the registered handler.", AMPS_E_USAGE));\
387 }\
388 catch(...)\
389 {\
390 ;\
391 }\
392 }*/
393
394 #define AMPS_CALL_EXCEPTION_WRAPPER_STREAM_FULL_2(me, x)\
395 while(me->_connected)\
396 {\
397 try\
398 {\
399 x;\
400 break;\
401 }\
402 catch(MessageStreamFullException& msfEx_)\
403 {\
404 try\
405 {\
406 me->checkAndSendHeartbeat(false);\
407 }\
408 catch (std::exception& stdEx_)\
409 {\
410 try\
411 {\
412 me->_exceptionListener->exceptionThrown(stdEx_);\
413 }\
414 catch(...)\
415 {\
416 ;\
417 }\
418 break;\
419 }\
420 }\
421 }
422 #endif
423
424 #define AMPS_UNHANDLED_EXCEPTION(ex) \
425 try\
426 {\
427 _exceptionListener->exceptionThrown(ex);\
428 }\
429 catch(...)\
430 {\
431 ;\
432 }
433
434 #define AMPS_UNHANDLED_EXCEPTION_2(me,ex) \
435 try\
436 {\
437 me->_exceptionListener->exceptionThrown(ex);\
438 }\
439 catch(...)\
440 {\
441 ;\
442 }
443
444
445 class Client;
446
471
472 class Command
473 {
474 Message _message;
475 unsigned _timeout;
476 unsigned _batchSize;
477 unsigned _flags;
478 static const unsigned Subscribe = 1;
479 static const unsigned SOW = 2;
480 static const unsigned NeedsSequenceNumber = 4;
481 static const unsigned ProcessedAck = 8;
482 static const unsigned StatsAck = 16;
483 void init(Message::Command::Type command_)
484 {
485 _timeout = 0;
486 _batchSize = 0;
487 _flags = 0;
488 _message.reset();
489 _message.setCommandEnum(command_);
490 _setIds();
491 }
492 void init(const std::string& command_)
493 {
494 _timeout = 0;
495 _batchSize = 0;
496 _flags = 0;
497 _message.reset();
498 _message.setCommand(command_);
499 _setIds();
500 }
501 void init(const char* command_, size_t commandLen_)
502 {
503 _timeout = 0;
504 _batchSize = 0;
505 _flags = 0;
506 _message.reset();
507 _message.setCommand(command_, commandLen_);
508 _setIds();
509 }
510 void _setIds(void)
511 {
512 Message::Command::Type command = _message.getCommandEnum();
513 if (!(command & Message::Command::NoDataCommands))
514 {
515 _message.newCommandId();
516 if (command == Message::Command::Subscribe ||
517 command == Message::Command::SOWAndSubscribe ||
518 command == Message::Command::DeltaSubscribe ||
519 command == Message::Command::SOWAndDeltaSubscribe)
520 {
521 _message.setSubscriptionId(_message.getCommandId());
522 _flags |= Subscribe;
523 }
524 if (command == Message::Command::SOW
525 || command == Message::Command::SOWAndSubscribe
526 || command == Message::Command::SOWAndDeltaSubscribe)
527 {
528 _message.setQueryID(_message.getCommandId());
529 if (_batchSize == 0)
530 {
531 setBatchSize(AMPS_DEFAULT_BATCH_SIZE);
532 }
533 if (command == Message::Command::SOW)
534 {
535 _flags |= SOW;
536 }
537 }
538 _flags |= ProcessedAck;
539 }
540 else if (command == Message::Command::SOWDelete)
541 {
542 _message.newCommandId();
543 _flags |= ProcessedAck;
544 _flags |= NeedsSequenceNumber;
545 }
546 else if (command == Message::Command::Publish
547 || command == Message::Command::DeltaPublish)
548 {
549 _flags |= NeedsSequenceNumber;
550 }
551 else if (command == Message::Command::StopTimer)
552 {
553 _message.newCommandId();
554 }
555 }
556 public:
560 Command(const std::string& command_)
561 {
562 init(command_);
563 }
568 Command(const char* command_, size_t commandLen_)
569 {
570 init(command_, commandLen_);
571 }
575 Command(Message::Command::Type command_)
576 {
577 init(command_);
578 }
579
583 Command& reset(const std::string& command_)
584 {
585 init(command_);
586 return *this;
587 }
592 Command& reset(const char* command_, size_t commandLen_)
593 {
594 init(command_, commandLen_);
595 return *this;
596 }
600 Command& reset(Message::Command::Type command_)
601 {
602 init(command_);
603 return *this;
604 }
612 Command& setSowKey(const std::string& sowKey_)
613 {
614 _message.setSowKey(sowKey_);
615 return *this;
616 }
625 Command& setSowKey(const char* sowKey_, size_t sowKeyLen_)
626 {
627 _message.setSowKey(sowKey_, sowKeyLen_);
628 return *this;
629 }
642 Command& setSowKeys(const std::string& sowKeys_)
643 {
644 _message.setSowKeys(sowKeys_);
645 return *this;
646 }
660 Command& setSowKeys(const char* sowKeys_, size_t sowKeysLen_)
661 {
662 _message.setSowKeys(sowKeys_, sowKeysLen_);
663 return *this;
664 }
666 Command& setCommandId(const std::string& cmdId_)
667 {
668 _message.setCommandId(cmdId_);
669 return *this;
670 }
673 Command& setCommandId(const char* cmdId_, size_t cmdIdLen_)
674 {
675 _message.setCommandId(cmdId_, cmdIdLen_);
676 return *this;
677 }
679 Command& setTopic(const std::string& topic_)
680 {
681 _message.setTopic(topic_);
682 return *this;
683 }
686 Command& setTopic(const char* topic_, size_t topicLen_)
687 {
688 _message.setTopic(topic_, topicLen_);
689 return *this;
690 }
692 Command& setFilter(const std::string& filter_)
693 {
694 _message.setFilter(filter_);
695 return *this;
696 }
699 Command& setFilter(const char* filter_, size_t filterLen_)
700 {
701 _message.setFilter(filter_, filterLen_);
702 return *this;
703 }
705 Command& setOrderBy(const std::string& orderBy_)
706 {
707 _message.setOrderBy(orderBy_);
708 return *this;
709 }
712 Command& setOrderBy(const char* orderBy_, size_t orderByLen_)
713 {
714 _message.setOrderBy(orderBy_, orderByLen_);
715 return *this;
716 }
718 Command& setSubId(const std::string& subId_)
719 {
720 _message.setSubscriptionId(subId_);
721 return *this;
722 }
725 Command& setSubId(const char* subId_, size_t subIdLen_)
726 {
727 _message.setSubscriptionId(subId_, subIdLen_);
728 return *this;
729 }
731 Command& setQueryId(const std::string& queryId_)
732 {
733 _message.setQueryId(queryId_);
734 return *this;
735 }
738 Command& setQueryId(const char* queryId_, size_t queryIdLen_)
739 {
740 _message.setQueryId(queryId_, queryIdLen_);
741 return *this;
742 }
748 Command& setBookmark(const std::string& bookmark_)
749 {
750 _message.setBookmark(bookmark_);
751 return *this;
752 }
759 Command& setBookmark(const char* bookmark_, size_t bookmarkLen_)
760 {
761 _message.setBookmark(bookmark_, bookmarkLen_);
762 return *this;
763 }
770 Command& setCorrelationId(const std::string& correlationId_)
771 {
772 _message.setCorrelationId(correlationId_);
773 return *this;
774 }
782 Command& setCorrelationId(const char* correlationId_, size_t correlationIdLen_)
783 {
784 _message.setCorrelationId(correlationId_, correlationIdLen_);
785 return *this;
786 }
789 Command& setOptions(const std::string& options_)
790 {
791 _message.setOptions(options_);
792 return *this;
793 }
797 Command& setOptions(const char* options_, size_t optionsLen_)
798 {
799 _message.setOptions(options_, optionsLen_);
800 return *this;
801 }
803 Command& setSequence(const std::string& seq_)
804 {
805 _message.setSequence(seq_);
806 return *this;
807 }
810 Command& setSequence(const char* seq_, size_t seqLen_)
811 {
812 _message.setSequence(seq_, seqLen_);
813 return *this;
814 }
816 Command& setSequence(const amps_uint64_t seq_)
817 {
818 std::ostringstream os;
819 os << seq_;
820 _message.setSequence(os.str());
821 return *this;
822 }
823 amps_uint64_t getSequence() const
824 {
825 return amps_message_get_field_uint64(_message.getMessage(), AMPS_Sequence);
826 }
829 Command& setData(const std::string& data_)
830 {
831 _message.setData(data_);
832 return *this;
833 }
837 Command& setData(const char* data_, size_t dataLen_)
838 {
839 _message.setData(data_, dataLen_);
840 return *this;
841 }
851 Command& setTimeout(unsigned timeout_)
852 {
853 _timeout = timeout_;
854 return *this;
855 }
857 Command& setTopN(unsigned topN_)
858 {
859 if (topN_ != (unsigned)AMPS_DEFAULT_TOP_N)
860 {
861 _message.setTopNRecordsReturned(topN_);
862 }
863 else
864 {
865 _message.setTopNRecordsReturned(nullptr, 0);
866 }
867 return *this;
868 }
873 Command& setBatchSize(unsigned batchSize_)
874 {
875 _message.setBatchSize(batchSize_);
876 _batchSize = batchSize_;
877 return *this;
878 }
889 Command& setExpiration(unsigned expiration_)
890 {
891 _message.setExpiration(expiration_);
892 return *this;
893 }
895 Command& addAckType(const std::string& ackType_)
896 {
897 _message.setAckType(_message.getAckType() + "," + ackType_);
898 if (ackType_ == "processed")
899 {
900 _flags |= ProcessedAck;
901 }
902 else if (ackType_ == "stats")
903 {
904 _flags |= StatsAck;
905 }
906 return *this;
907 }
909 Command& setAckType(const std::string& ackType_)
910 {
911 _message.setAckType(ackType_);
912 if (ackType_.find("processed") != std::string::npos)
913 {
914 _flags |= ProcessedAck;
915 }
916 else
917 {
918 _flags &= ~ProcessedAck;
919 }
920 if (ackType_.find("stats") != std::string::npos)
921 {
922 _flags |= StatsAck;
923 }
924 else
925 {
926 _flags &= ~StatsAck;
927 }
928 return *this;
929 }
931 Command& setAckType(unsigned ackType_)
932 {
933 _message.setAckTypeEnum(ackType_);
934 if (ackType_ & Message::AckType::Processed)
935 {
936 _flags |= ProcessedAck;
937 }
938 else
939 {
940 _flags &= ~ProcessedAck;
941 }
942 if (ackType_ & Message::AckType::Stats)
943 {
944 _flags |= StatsAck;
945 }
946 else
947 {
948 _flags &= ~StatsAck;
949 }
950 return *this;
951 }
953 std::string getAckType() const
954 {
955 return (std::string)(_message.getAckType());
956 }
958 unsigned getAckTypeEnum() const
959 {
960 return _message.getAckTypeEnum();
961 }
962
963 Message& getMessage(void)
964 {
965 return _message;
966 }
967 unsigned getTimeout(void) const
968 {
969 return _timeout;
970 }
971 unsigned getBatchSize(void) const
972 {
973 return _batchSize;
974 }
975 bool isSubscribe(void) const
976 {
977 return _flags & Subscribe;
978 }
979 bool isSow(void) const
980 {
981 return (_flags & SOW) != 0;
982 }
983 bool hasProcessedAck(void) const
984 {
985 return (_flags & ProcessedAck) != 0;
986 }
987 bool hasStatsAck(void) const
988 {
989 return (_flags & StatsAck) != 0;
990 }
991 bool needsSequenceNumber(void) const
992 {
993 return (_flags & NeedsSequenceNumber) != 0;
994 }
995 };
996
999 typedef void(*DisconnectHandlerFunc)(Client&, void* userData);
1000
1001 class Message;
1002 typedef Handler<DisconnectHandlerFunc, Client&> DisconnectHandler;
1003
1006 class Authenticator
1007 {
1008 public:
1009 virtual ~Authenticator() {;}
1010
1016 virtual std::string authenticate(const std::string& userName_, const std::string& password_) = 0;
1024 virtual std::string retry(const std::string& userName_, const std::string& password_) = 0;
1031 virtual void completed(const std::string& userName_, const std::string& password_, const std::string& reason_) = 0;
1032 };
1033
1037 class DefaultAuthenticator : public Authenticator
1038 {
1039 public:
1040 virtual ~DefaultAuthenticator() {;}
1043 std::string authenticate(const std::string& /*userName_*/, const std::string& password_)
1044 {
1045 return password_;
1046 }
1047
1050 std::string retry(const std::string& /*userName_*/, const std::string& /*password_*/)
1051 {
1052 throw AuthenticationException("retry not implemented by DefaultAuthenticator.");
1053 }
1054
1055 void completed(const std::string& /*userName_*/, const std::string& /* password_ */, const std::string& /* reason */) {;}
1056
1060 static Authenticator& instance()
1061 {
1062 static DefaultAuthenticator d; // -V1096
1063 return d;
1064 }
1065 };
1066
1069 class StoreReplayer
1070 {
1071 public:
1072
1076 virtual void execute(Message& message_) = 0;
1077
1078 virtual ~StoreReplayer() {;}
1079 };
1080
1081 class Store;
1082
1091 typedef bool (*PublishStoreResizeHandler)(Store store_,
1092 size_t size_,
1093 void* userData_);
1094
1097 class StoreImpl : public RefBody
1098 {
1099 public:
1105 StoreImpl(bool errorOnPublishGap_ = false)
1106 : _resizeHandler(NULL)
1107 , _resizeHandlerData(NULL)
1108 , _errorOnPublishGap(errorOnPublishGap_)
1109 {;}
1110
1115 virtual amps_uint64_t store(const Message& message_) = 0;
1116
1123 virtual void discardUpTo(amps_uint64_t index_) = 0;
1124
1129 virtual void replay(StoreReplayer& replayer_) = 0;
1130
1138 virtual bool replaySingle(StoreReplayer& replayer_, amps_uint64_t index_) = 0;
1139
1144 virtual size_t unpersistedCount() const = 0;
1145
1146 virtual ~StoreImpl() {;}
1147
1156 virtual void flush(long timeout_) = 0;
1157
1160 static inline size_t getUnsetPosition()
1161 {
1162 return AMPS_UNSET_INDEX;
1163 }
1164
1167 static inline amps_uint64_t getUnsetSequence()
1168 {
1169 return AMPS_UNSET_SEQUENCE;
1170 }
1171
1175 virtual amps_uint64_t getLowestUnpersisted() const = 0;
1176
1180 virtual amps_uint64_t getLastPersisted() = 0;
1181
1191 inline virtual void setResizeHandler(PublishStoreResizeHandler handler_,
1192 void* userData_)
1193 {
1194 _resizeHandler = handler_;
1195 _resizeHandlerData = userData_;
1196 }
1197
1198 inline virtual PublishStoreResizeHandler getResizeHandler() const
1199 {
1200 return _resizeHandler;
1201 }
1202
1203 bool callResizeHandler(size_t newSize_);
1204
1205 inline virtual void setErrorOnPublishGap(bool errorOnPublishGap_)
1206 {
1207 _errorOnPublishGap = errorOnPublishGap_;
1208 }
1209
1210 inline virtual bool getErrorOnPublishGap() const
1211 {
1212 return _errorOnPublishGap;
1213 }
1214
1215 private:
1216 PublishStoreResizeHandler _resizeHandler;
1217 void* _resizeHandlerData;
1218 bool _errorOnPublishGap;
1219 };
1220
1223 class Store
1224 {
1225 RefHandle<StoreImpl> _body;
1226 public:
1227 Store() = default;
1228 Store(StoreImpl* body_) : _body(body_) {;}
1229 Store(const Store& rhs) : _body(rhs._body) {;}
1230 Store& operator=(const Store& rhs)
1231 {
1232 _body = rhs._body;
1233 return *this;
1234 }
1235
1239 amps_uint64_t store(const Message& message_)
1240 {
1241 return _body.get().store(message_);
1242 }
1243
1250 void discardUpTo(amps_uint64_t index_)
1251 {
1252 _body.get().discardUpTo(index_);
1253 }
1254
1259 void replay(StoreReplayer& replayer_)
1260 {
1261 _body.get().replay(replayer_);
1262 }
1263
1271 bool replaySingle(StoreReplayer& replayer_, amps_uint64_t index_)
1272 {
1273 return _body.get().replaySingle(replayer_, index_);
1274 }
1275
1280 size_t unpersistedCount() const
1281 {
1282 return _body.get().unpersistedCount();
1283 }
1284
1288 bool isValid() const
1289 {
1290 return _body.isValid();
1291 }
1292
1301 void flush(long timeout_ = 0)
1302 {
1303 return _body.get().flush(timeout_);
1304 }
1305
1309 amps_uint64_t getLowestUnpersisted()
1310 {
1311 return _body.get().getLowestUnpersisted();
1312 }
1313
1317 amps_uint64_t getLastPersisted()
1318 {
1319 return _body.get().getLastPersisted();
1320 }
1321
1331 void setResizeHandler(PublishStoreResizeHandler handler_,
1332 void* userData_)
1333 {
1334 _body.get().setResizeHandler(handler_, userData_);
1335 }
1336
1337 PublishStoreResizeHandler getResizeHandler() const
1338 {
1339 return _body.get().getResizeHandler();
1340 }
1341
1346 inline void setErrorOnPublishGap(bool errorOnPublishGap_)
1347 {
1348 _body.get().setErrorOnPublishGap(errorOnPublishGap_);
1349 }
1350
1355 inline bool getErrorOnPublishGap() const
1356 {
1357 return _body.get().getErrorOnPublishGap();
1358 }
1359
1363 StoreImpl* get()
1364 {
1365 if (_body.isValid())
1366 {
1367 return &_body.get();
1368 }
1369 else
1370 {
1371 return NULL;
1372 }
1373 }
1374
1375 };
1376
1381 class FailedWriteHandler
1382 {
1383 public:
1384 virtual ~FailedWriteHandler() {;}
1391 virtual void failedWrite(const Message& message_,
1392 const char* reason_, size_t reasonLength_) = 0;
1393 };
1394
1395
1396 inline bool StoreImpl::callResizeHandler(size_t newSize_)
1397 {
1398 if (_resizeHandler)
1399 {
1400 return _resizeHandler(Store(this), newSize_, _resizeHandlerData);
1401 }
1402 return true;
1403 }
1404
1411 inline bool DangerousFlushPublishStoreResizeHandler(Store store_, size_t /*size_*/,
1412 void* data_)
1413 {
1414 long* timeoutp = (long*)data_;
1415 size_t count = store_.unpersistedCount();
1416 if (count == 0)
1417 {
1418 return false;
1419 }
1420 try
1421 {
1422 store_.flush(*timeoutp);
1423 }
1424 #ifdef _WIN32
1425 catch (const TimedOutException&)
1426 #else
1427 catch (const TimedOutException& e)
1428 #endif
1429 {
1430 return true;
1431 }
1432 return (count == store_.unpersistedCount());
1433 }
1434
1439 class FailedResubscribeHandler
1440 {
1441 public:
1453 virtual bool failure(const Message& message_, const MessageHandler& handler_,
1454 unsigned requestedAckTypes_,
1455 const AMPSException& exception_) = 0;
1456 };
1457
1461 class SubscriptionManager
1462 {
1463 public:
1464 virtual ~SubscriptionManager() {;}
1474 virtual AMPS_ATOMIC_BASE_TYPE subscribe(MessageHandler messageHandler_, const Message& message_,
1475 unsigned requestedAckTypes_) = 0;
1479 virtual AMPS_ATOMIC_BASE_TYPE getGenerationCount() const
1480 {
1481 return 0;
1482 }
1486 virtual void unsubscribe(const Message::Field& subId_) = 0;
1489 virtual void clear() = 0;
1493 virtual void resubscribe(Client& client_) = 0;
1498 virtual void setFailedResubscribeHandler(std::shared_ptr<FailedResubscribeHandler> handler_)
1499 {
1500 _failedResubscribeHandler = handler_;
1501 }
1502 protected:
1503 std::shared_ptr<FailedResubscribeHandler> _failedResubscribeHandler;
1504 };
1505
1509
1510 class ConnectionStateListener
1511 {
1512 public:
1514 typedef enum { Disconnected = 0,
1515 Shutdown = 1,
1516 Connected = 2,
1517 LoggedOn = 4,
1518 PublishReplayed = 8,
1519 HeartbeatInitiated = 16,
1520 Resubscribed = 32,
1521 UNKNOWN = 16384
1522 } State;
1523
1533 virtual void connectionStateChanged(State newState_) = 0;
1534 virtual ~ConnectionStateListener() {;}
1535 };
1536
1537
1538 class MessageStreamImpl;
1539 class MessageStream;
1540
1541 typedef void(*DeferredExecutionFunc)(void*);
1542
1543 class ClientImpl : public RefBody // -V553
1544 {
1545 // Class to wrap turning of Nagle for things like flush and logon
1546 class NoDelay
1547 {
1548 private:
1549 AMPS_SOCKET _socket;
1550 int _noDelay;
1551 char* _valuePtr;
1552 #ifdef _WIN32
1553 int _valueLen;
1554 #else
1555 socklen_t _valueLen;
1556 #endif
1557 public:
1558 NoDelay(amps_handle client_)
1559 : _socket(AMPS_INVALID_SOCKET), _noDelay(0), _valueLen(sizeof(int))
1560 {
1561 _valuePtr = (char*)&_noDelay;
1562 _socket = amps_client_get_socket(client_);
1563 if (_socket != AMPS_INVALID_SOCKET)
1564 {
1565 getsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, _valuePtr, &_valueLen);
1566 if (!_noDelay)
1567 {
1568 _noDelay = 1;
1569 setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, _valuePtr, _valueLen);
1570 }
1571 else
1572 {
1573 _socket = AMPS_INVALID_SOCKET;
1574 }
1575 }
1576 }
1577
1578 ~NoDelay()
1579 {
1580 if (_socket != AMPS_INVALID_SOCKET)
1581 {
1582 _noDelay = 0;
1583 setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, _valuePtr, _valueLen);
1584 }
1585 }
1586 };
1587
1588 friend class Client;
1589 protected:
1590 amps_handle _client;
1591 DisconnectHandler _disconnectHandler;
1592 enum GlobalCommandTypeHandlers : size_t
1593 {
1594 Publish = 0,
1595 SOW = 1,
1596 GroupBegin = 2,
1597 GroupEnd = 3,
1598 Heartbeat = 4,
1599 OOF = 5,
1600 Ack = 6,
1601 LastChance = 7,
1602 DuplicateMessage = 8,
1603 COUNT = 9
1604 };
1605 std::vector<MessageHandler> _globalCommandTypeHandlers;
1606 Message _beatMessage, _readMessage;
1607 MessageRouter _routes;
1608 MessageRouter::RouteCache _routeCache;
1609 mutable Mutex _lock;
1610 std::string _name, _nameHash, _connectionName, _lastUri, _logonCorrelationData, _preflightMessage;
1611 std::vector<std::string> _httpPreflightHeaders;
1612 amps_uint64_t _nameHashValue;
1613 BookmarkStore _bookmarkStore;
1614 Store _publishStore;
1615 bool _isRetryOnDisconnect;
1616 amps_unique_ptr<FailedWriteHandler> _failedWriteHandler;
1617 #if __cplusplus >= 201100L || _MSC_VER >= 1900
1618 std::atomic<amps_uint64_t> _lastSentHaSequenceNumber;
1619 #else
1620 volatile amps_uint64_t _lastSentHaSequenceNumber;
1621 #endif
1622 AMPS_ATOMIC_TYPE_8 _logonInProgress;
1623 AMPS_ATOMIC_TYPE _subscriptionManagerGenerationCount;
1624 VersionInfo _serverVersion;
1625 Timer _heartbeatTimer;
1626 amps_unique_ptr<MessageStream> _pEmptyMessageStream;
1627
1628 // queue data
1629 int _queueAckTimeout;
1630 bool _isAutoAckEnabled;
1631 unsigned _ackBatchSize;
1632 unsigned _queuedAckCount;
1633 unsigned _defaultMaxDepth;
1634 struct QueueBookmarks
1635 {
1636 QueueBookmarks(const std::string& topic_)
1637 : _topic(topic_)
1638 , _oldestTime(0)
1639 , _bookmarkCount(0)
1640 {;}
1641 std::string _topic;
1642 std::string _data;
1643 amps_uint64_t _oldestTime;
1644 unsigned _bookmarkCount;
1645 };
1646 typedef amps_uint64_t topic_hash;
1647 typedef std::map<topic_hash, QueueBookmarks> TopicHashMap;
1648 TopicHashMap _topicHashMap;
1649
1650 class ClientStoreReplayer : public StoreReplayer
1651 {
1652 ClientImpl* _client;
1653 public:
1654 unsigned _version;
1655 amps_result _res;
1656
1657 ClientStoreReplayer()
1658 : _client(NULL), _version(0), _res(AMPS_E_OK)
1659 {}
1660
1661 ClientStoreReplayer(ClientImpl* client_)
1662 : _client(client_), _version(0), _res(AMPS_E_OK)
1663 {}
1664
1665 void setClient(ClientImpl* client_)
1666 {
1667 _client = client_;
1668 }
1669
1670 void execute(Message& message_)
1671 {
1672 if (!_client)
1673 {
1674 throw CommandException("Can't replay without a client.");
1675 }
1676 amps_uint64_t index = amps_message_get_field_uint64(message_.getMessage(),
1677 AMPS_Sequence);
1678 if (index > _client->_lastSentHaSequenceNumber)
1679 {
1680 _client->_lastSentHaSequenceNumber = index;
1681 }
1682
1683 _res = AMPS_E_OK;
1684 // Don't replay a queue cancel message after a reconnect.
1685 // Currently, the only messages that will have anything in options
1686 // are cancel messages.
1687 if (!message_.getCommand().empty() &&
1688 (!_client->_logonInProgress ||
1689 message_.getOptions().len() < 6))
1690 {
1691 _res = amps_client_send_batch(_client->_client,
1692 message_.getMessage(),
1693 &_version,
1694 1);
1695 if (_res != AMPS_E_OK)
1696 {
1697 throw DisconnectedException("AMPS Server disconnected during replay");
1698 }
1699 }
1700 }
1701
1702 };
1703 ClientStoreReplayer _replayer;
1704
1705 class FailedWriteStoreReplayer : public StoreReplayer
1706 {
1707 ClientImpl* _parent;
1708 const char* _reason;
1709 size_t _reasonLength;
1710 size_t _replayCount;
1711 public:
1712 FailedWriteStoreReplayer(ClientImpl* parent, const char* reason_, size_t reasonLength_)
1713 : _parent(parent),
1714 _reason(reason_),
1715 _reasonLength(reasonLength_),
1716 _replayCount(0)
1717 {;}
1718 void execute(Message& message_)
1719 {
1720 if (_parent->_failedWriteHandler)
1721 {
1722 ++_replayCount;
1723 _parent->_failedWriteHandler->failedWrite(message_,
1724 _reason, _reasonLength);
1725 }
1726 }
1727 size_t replayCount(void) const
1728 {
1729 return _replayCount;
1730 }
1731 };
1732
1733 struct AckResponseImpl : public RefBody
1734 {
1735 std::string username, password, reason, status, bookmark, options;
1736 amps_uint64_t sequenceNo;
1737 amps_uint64_t nameHashValue;
1738 VersionInfo serverVersion;
1739 #if __cplusplus >= 201100L || _MSC_VER >= 1900
1740 std::atomic<bool> responded;
1741 std::atomic<bool> abandoned;
1742 #else
1743 volatile bool responded;
1744 volatile bool abandoned;
1745 #endif
1746 unsigned connectionVersion;
1747 AckResponseImpl() :
1748 RefBody(),
1749 username(), password(), reason(), status(), bookmark(), options(),
1750 sequenceNo((amps_uint64_t)0),
1751 nameHashValue((amps_uint64_t)0),
1752 serverVersion(),
1753 responded(false),
1754 abandoned(false),
1755 connectionVersion(UINT_MAX) // Don't abandon if unsent AC-1329
1756 {
1757 }
1758 };
1759
1760 class AckResponse
1761 {
1762 RefHandle<AckResponseImpl> _body;
1763 public:
1764 AckResponse() : _body(NULL) {;}
1765 AckResponse(const AckResponse& rhs) : _body(rhs._body) {;}
1766 static AckResponse create()
1767 {
1768 AckResponse r;
1769 r._body = new AckResponseImpl();
1770 return r;
1771 }
1772
1773 const std::string& username()
1774 {
1775 return _body.get().username;
1776 }
1777 void setUsername(const char* data_, size_t len_)
1778 {
1779 if (data_)
1780 {
1781 _body.get().username.assign(data_, len_);
1782 }
1783 else
1784 {
1785 _body.get().username.clear();
1786 }
1787 }
1788 const std::string& password()
1789 {
1790 return _body.get().password;
1791 }
1792 void setPassword(const char* data_, size_t len_)
1793 {
1794 if (data_)
1795 {
1796 _body.get().password.assign(data_, len_);
1797 }
1798 else
1799 {
1800 _body.get().password.clear();
1801 }
1802 }
1803 const std::string& reason()
1804 {
1805 return _body.get().reason;
1806 }
1807 void setReason(const char* data_, size_t len_)
1808 {
1809 if (data_)
1810 {
1811 _body.get().reason.assign(data_, len_);
1812 }
1813 else
1814 {
1815 _body.get().reason.clear();
1816 }
1817 }
1818 const std::string& status()
1819 {
1820 return _body.get().status;
1821 }
1822 void setStatus(const char* data_, size_t len_)
1823 {
1824 if (data_)
1825 {
1826 _body.get().status.assign(data_, len_);
1827 }
1828 else
1829 {
1830 _body.get().status.clear();
1831 }
1832 }
1833 const std::string& bookmark()
1834 {
1835 return _body.get().bookmark;
1836 }
1837 void setBookmark(const Field& bookmark_)
1838 {
1839 AckResponseImpl& body = _body.get();
1840 if (!bookmark_.empty())
1841 {
1842 body.bookmark.assign(bookmark_.data(), bookmark_.len());
1843 Field::parseBookmark(bookmark_, body.nameHashValue,
1844 body.sequenceNo);
1845 }
1846 else
1847 {
1848 body.bookmark.clear();
1849 body.sequenceNo = (amps_uint64_t)0;
1850 body.nameHashValue = (amps_uint64_t)0;
1851 }
1852 }
1853 amps_uint64_t sequenceNo() const
1854 {
1855 return _body.get().sequenceNo;
1856 }
1857 amps_uint64_t nameHashValue() const
1858 {
1859 return _body.get().nameHashValue;
1860 }
1861 void setSequenceNo(const char* data_, size_t len_)
1862 {
1863 amps_uint64_t result = (amps_uint64_t)0;
1864 if (data_)
1865 {
1866 for (size_t i = 0; i < len_; ++i)
1867 {
1868 result *= (amps_uint64_t)10;
1869 result += (amps_uint64_t)(data_[i] - '0');
1870 }
1871 }
1872 _body.get().sequenceNo = result;
1873 }
1874 VersionInfo serverVersion() const
1875 {
1876 return _body.get().serverVersion;
1877 }
1878 void setServerVersion(const char* data_, size_t len_)
1879 {
1880 if (data_)
1881 {
1882 _body.get().serverVersion.setVersion(std::string(data_, len_));
1883 }
1884 }
1885 bool responded()
1886 {
1887 return _body.get().responded;
1888 }
1889 void setResponded()
1890 {
1891 _body.get().responded = true;
1892 }
1893 bool abandoned()
1894 {
1895 return _body.get().abandoned;
1896 }
1897 void setAbandoned()
1898 {
1899 if (_body.isValid())
1900 {
1901 _body.get().abandoned = true;
1902 }
1903 }
1904
1905 void setConnectionVersion(unsigned connectionVersion)
1906 {
1907 _body.get().connectionVersion = connectionVersion;
1908 }
1909
1910 unsigned getConnectionVersion()
1911 {
1912 return _body.get().connectionVersion;
1913 }
1914
1915 void setOptions(const char* data_, size_t len_)
1916 {
1917 if (data_)
1918 {
1919 _body.get().options.assign(data_, len_);
1920 }
1921 else
1922 {
1923 _body.get().options.clear();
1924 }
1925 }
1926
1927 const std::string& options()
1928 {
1929 return _body.get().options;
1930 }
1931
1932 AckResponse& operator=(const AckResponse& rhs)
1933 {
1934 _body = rhs._body;
1935 return *this;
1936 }
1937 };
1938
1939
1940 typedef std::map<std::string, AckResponse> AckMap;
1941 AckMap _ackMap;
1942 Mutex _ackMapLock;
1943 DefaultExceptionListener _defaultExceptionListener;
1944 protected:
1945
1946 struct DeferredExecutionRequest
1947 {
1948 DeferredExecutionRequest(DeferredExecutionFunc func_,
1949 void* userData_)
1950 : _func(func_),
1951 _userData(userData_)
1952 {;}
1953
1954 DeferredExecutionFunc _func;
1955 void* _userData;
1956 };
1957 const ExceptionListener* _exceptionListener;
1958 std::shared_ptr<const ExceptionListener> _pExceptionListener;
1959 amps_unique_ptr<SubscriptionManager> _subscriptionManager;
1960 volatile bool _connected;
1961 std::string _username;
1962 typedef std::set<ConnectionStateListener*> ConnectionStateListeners;
1963 ConnectionStateListeners _connectionStateListeners;
1964 typedef std::vector<DeferredExecutionRequest> DeferredExecutionList;
1965 Mutex _deferredExecutionLock;
1966 DeferredExecutionList _deferredExecutionList;
1967 unsigned _heartbeatInterval;
1968 unsigned _readTimeout;
1969
1970 void broadcastConnectionStateChanged(ConnectionStateListener::State newState_)
1971 {
1972 // If we disconnected before we got to notification, don't notify.
1973 // This should only be able to happen for Resubscribed, since the lock
1974 // is released to let the subscription manager run resubscribe so a
1975 // disconnect could be called before the change is broadcast.
1976 if (!_connected && newState_ > ConnectionStateListener::Connected)
1977 {
1978 return;
1979 }
1980 for (ConnectionStateListeners::iterator it = _connectionStateListeners.begin(); it != _connectionStateListeners.end(); ++it)
1981 {
1982 AMPS_CALL_EXCEPTION_WRAPPER(
1983 (*it)->connectionStateChanged(newState_));
1984 }
1985 }
1986 void captureSubscriptionManagerGenerationCount()
1987 {
1988 if (_subscriptionManager)
1989 {
1990 _subscriptionManagerGenerationCount = _subscriptionManager->getGenerationCount();
1991 }
1992 }
1993
1994 unsigned processedAck(Message& message);
1995 unsigned persistedAck(Message& meesage);
1996 void lastChance(Message& message);
1997 void checkAndSendHeartbeat(bool force = false);
1998 virtual ConnectionInfo getConnectionInfo() const;
1999 static amps_result
2000 ClientImplMessageHandler(amps_handle message, void* userData);
2001 static void
2002 ClientImplPreDisconnectHandler(amps_handle client, unsigned failedConnectionVersion, void* userData);
2003 static amps_result
2004 ClientImplDisconnectHandler(amps_handle client, void* userData);
2005 static const char*
2006 ClientImplGetHttpPreflightMessage(void* userData);
2007
2008 void unsubscribeInternal(const std::string& id)
2009 {
2010 if (id.empty())
2011 {
2012 return;
2013 }
2014 // remove the handler first to avoid any more message delivery
2015 Message::Field subId;
2016 subId.assign(id.data(), id.length());
2017 _routes.removeRoute(subId);
2018 // Lock is already acquired
2019 if (_subscriptionManager)
2020 {
2021 // Have to unlock before calling into sub manager to avoid deadlock
2022 Unlock<Mutex> unlock(_lock);
2023 _subscriptionManager->unsubscribe(subId);
2024 }
2025 threadLocalMessage.reset();
2026 threadLocalMessage.setCommandEnum(Message::Command::Unsubscribe);
2027 threadLocalMessage.newCommandId();
2028 threadLocalMessage.setSubscriptionId(id);
2029 _sendWithoutRetry(threadLocalMessage);
2030 deferredExecution(&amps_noOpFn, NULL);
2031 }
2032
2033 AckResponse syncAckProcessing(long timeout_, Message& message_,
2034 amps_uint64_t haSeq = (amps_uint64_t)0,
2035 AMPS_ATOMIC_BASE_TYPE subscribeGeneration_ = 0)
2036 {
2037 bool isLogon = message_.getCommand() == "logon";
2038 if (_logonInProgress && !isLogon)
2039 {
2040 while (_logonInProgress)
2041 {
2042 if (!_lock.wait(200))
2043 {
2044 amps_invoke_waiting_function();
2045 if (!_isRetryOnDisconnect)
2046 {
2047 // retrySend is disabled so throw the error
2048 AMPSException::throwFor(_client, AMPS_E_RETRY);
2049 }
2050 }
2051 }
2052 }
2053 // inv: we already have _lock locked up.
2054 AckResponse ack = AckResponse::create();
2055 if (1)
2056 {
2057 Lock<Mutex> guard(_ackMapLock);
2058 _ackMap[message_.getCommandId()] = ack;
2059 }
2060 if (subscribeGeneration_ == 0)
2061 {
2062 ack.setConnectionVersion((unsigned)_sendSequenced(message_, haSeq, 0));
2063 }
2064 else
2065 {
2066 ack.setConnectionVersion((unsigned)_send(message_, subscribeGeneration_));
2067 }
2068 if (ack.getConnectionVersion() == 0)
2069 {
2070 // Send failed
2071 throw DisconnectedException("Connection closed while waiting for response.");
2072 }
2073 else if (ack.getConnectionVersion() == UINT_MAX)
2074 {
2075 ack.setResponded();
2076 Lock<Mutex> guard(_ackMapLock);
2077 AckMap::iterator i = _ackMap.find(message_.getCommandId());
2078 if (i != _ackMap.end())
2079 {
2080 _ackMap.erase(i);
2081 }
2082 return ack;
2083 }
2084 bool timedOut = false;
2085 AMPS_START_TIMER(timeout_)
2086 while (!timedOut && !ack.responded() && !ack.abandoned())
2087 {
2088 if (timeout_)
2089 {
2090 timedOut = !_lock.wait(timeout_);
2091 // May have woken up early, check real time
2092 if (timedOut)
2093 {
2094 AMPS_RESET_TIMER(timedOut, timeout_);
2095 }
2096 }
2097 else
2098 {
2099 // Using a timeout version to ensure python can interrupt
2100 _lock.wait(1000);
2101 Unlock<Mutex> unlck(_lock);
2102 amps_invoke_waiting_function();
2103 }
2104 }
2105 if (ack.responded())
2106 {
2107 if (ack.status() != "failure")
2108 {
2109 if (message_.getCommand() == "logon")
2110 {
2111 amps_uint64_t ackSequence = ack.sequenceNo();
2112 if (_lastSentHaSequenceNumber < ackSequence)
2113 {
2114 _lastSentHaSequenceNumber = ackSequence;
2115 }
2116 if (_publishStore.isValid())
2117 {
2118 // If this throws, logon will fail and eitehr be
2119 // handled in HAClient/ServerChooser or by the caller
2120 // of logon.
2121 _publishStore.discardUpTo(ackSequence);
2122 if (_lastSentHaSequenceNumber < _publishStore.getLastPersisted())
2123 {
2124 _lastSentHaSequenceNumber = _publishStore.getLastPersisted();
2125 }
2126 }
2127 _nameHash = ack.bookmark().substr(0, ack.bookmark().find('|'));
2128 _nameHashValue = ack.nameHashValue();
2129 const std::string& ackOptions = ack.options();
2130 size_t start = ackOptions.find("connection_name=");
2131 if (start != std::string::npos)
2132 {
2133 start += 16;
2134 size_t end = ackOptions.find(',', start);
2135 if (end != std::string::npos)
2136 {
2137 end -= start;
2138 }
2139 _connectionName = ackOptions.substr(start, end);
2140 }
2141 _serverVersion = ack.serverVersion();
2142 if (_bookmarkStore.isValid())
2143 {
2144 _bookmarkStore.setServerVersion(_serverVersion);
2145 }
2146 }
2147 if (_ackBatchSize)
2148 {
2149 const std::string& options = ack.options();
2150 size_t index = options.find_first_of("max_backlog=");
2151 if (index != std::string::npos)
2152 {
2153 unsigned data = 0;
2154 const char* c = options.c_str() + index + 12;
2155 while (*c && *c != ',')
2156 {
2157 data = (data * 10) + (unsigned)(*c++ -48);
2158 }
2159 if (_ackBatchSize > data)
2160 {
2161 _ackBatchSize = data;
2162 }
2163 }
2164 }
2165 return ack;
2166 }
2167 const size_t NotEntitled = 12;
2168 std::string ackReason = ack.reason();
2169 if (ackReason.length() == 0)
2170 {
2171 return ack; // none
2172 }
2173 if (ackReason.length() == NotEntitled &&
2174 ackReason[0] == 'n' &&
2175 message_.getUserId().len() == 0)
2176 {
2177 message_.assignUserId(_username);
2178 }
2179 message_.throwFor(_client, ackReason);
2180 }
2181 else // !ack.responded()
2182 {
2183 if (!ack.abandoned())
2184 {
2185 throw TimedOutException("timed out waiting for operation.");
2186 }
2187 else
2188 {
2189 throw DisconnectedException("Connection closed while waiting for response.");
2190 }
2191 }
2192 return ack;
2193 }
2194
2195 void _cleanup(void)
2196 {
2197 if (!_client)
2198 {
2199 return;
2200 }
2201 amps_client_set_predisconnect_handler(_client, NULL, 0L);
2202 amps_client_set_disconnect_handler(_client, NULL, 0L);
2203 AMPS_CALL_EXCEPTION_WRAPPER(ClientImpl::disconnect());
2204 _pEmptyMessageStream.reset(NULL);
2205 amps_client_destroy(_client);
2206 _client = NULL;
2207 }
2208
2209 public:
2210
2211 ClientImpl(const std::string& clientName)
2212 : _client(NULL), _name(clientName)
2213 , _isRetryOnDisconnect(true)
2214 , _lastSentHaSequenceNumber((amps_uint64_t)0), _logonInProgress(0)
2215 , _subscriptionManagerGenerationCount(0), _serverVersion()
2216 , _queueAckTimeout(AMPS_DEFAULT_QUEUE_ACK_TIMEOUT)
2217 , _isAutoAckEnabled(false)
2218 , _ackBatchSize(0)
2219 , _queuedAckCount(0)
2220 , _defaultMaxDepth(0)
2221 , _connected(false)
2222 , _heartbeatInterval(0)
2223 , _readTimeout(0)
2224 {
2225 _replayer.setClient(this);
2226 _client = amps_client_create(clientName.c_str());
2227 amps_client_set_message_handler(_client,
2228 (amps_handler)ClientImpl::ClientImplMessageHandler,
2229 this);
2230 amps_client_set_predisconnect_handler(_client,
2231 (amps_predisconnect_handler)ClientImpl::ClientImplPreDisconnectHandler,
2232 this);
2233 amps_client_set_disconnect_handler(_client,
2234 (amps_handler)ClientImpl::ClientImplDisconnectHandler,
2235 this);
2236 amps_client_set_http_preflight_callback(_client,
2237 ClientImpl::ClientImplGetHttpPreflightMessage,
2238 this);
2239 _exceptionListener = &_defaultExceptionListener;
2240 for (size_t i = 0; i < GlobalCommandTypeHandlers::COUNT; ++i)
2241 {
2242 _globalCommandTypeHandlers.emplace_back(MessageHandler());
2243 }
2244 }
2245
2246 virtual ~ClientImpl()
2247 {
2248 _cleanup();
2249 }
2250
2251 const std::string& getName() const
2252 {
2253 return _name;
2254 }
2255
2256 const std::string& getNameHash() const
2257 {
2258 return _nameHash;
2259 }
2260
2261 const amps_uint64_t getNameHashValue() const
2262 {
2263 return _nameHashValue;
2264 }
2265
2266 const std::string& getConnectionName() const
2267 {
2268 return _connectionName;
2269 }
2270
2271 void setName(const std::string& name)
2272 {
2273 // This operation will fail if the client's
2274 // name is already set.
2275 amps_result result = amps_client_set_name(_client, name.c_str());
2276 if (result != AMPS_E_OK)
2277 {
2278 AMPSException::throwFor(_client, result);
2279 }
2280 _name = name;
2281 }
2282
2283 const std::string& getLogonCorrelationData() const
2284 {
2285 return _logonCorrelationData;
2286 }
2287
2288 void setLogonCorrelationData(const std::string& logonCorrelationData_)
2289 {
2290 _logonCorrelationData = logonCorrelationData_;
2291 }
2292
2293 size_t getServerVersion() const
2294 {
2295 return _serverVersion.getOldStyleVersion();
2296 }
2297
2298 VersionInfo getServerVersionInfo() const
2299 {
2300 return _serverVersion;
2301 }
2302
2303 const std::string& getURI() const
2304 {
2305 return _lastUri;
2306 }
2307
2308 virtual void connect(const std::string& uri)
2309 {
2310 Lock<Mutex> l(_lock);
2311 _connect(uri);
2312 }
2313
2314 virtual void _connect(const std::string& uri)
2315 {
2316 _lastUri = uri;
2317 amps_result result = amps_client_connect(_client, uri.c_str());
2318 if (result != AMPS_E_OK)
2319 {
2320 AMPSException::throwFor(_client, result);
2321 }
2322 threadLocalMessage.reset();
2323 _beatMessage.setCommandEnum(Message::Command::Heartbeat);
2324 _beatMessage.setOptions("beat");
2325 _readMessage.setClientImpl(this);
2326 if (_queueAckTimeout)
2327 {
2328 result = amps_client_set_idle_time(_client, _queueAckTimeout);
2329 if (result != AMPS_E_OK)
2330 {
2331 AMPSException::throwFor(_client, result);
2332 }
2333 }
2334 _connected = true;
2335 broadcastConnectionStateChanged(ConnectionStateListener::Connected);
2336 }
2337
2338 void addHttpPreflightHeader(const std::string& header_)
2339 {
2340 _httpPreflightHeaders.emplace_back(header_);
2341 }
2342
2343 void addHttpPreflightHeader(const std::string& key_, const std::string& value_)
2344 {
2345 _httpPreflightHeaders.emplace_back(key_ + std::string(": ") + value_);
2346 }
2347
2348 void clearHttpPreflightHeaders()
2349 {
2350 _httpPreflightHeaders.clear();
2351 }
2352
2353 template<class T>
2354 void setHttpPreflightHeaders(const T& headers_)
2355 {
2356 _httpPreflightHeaders.clear();
2357 for (typename T::const_iterator i = headers_.begin(); i != headers_.end(); ++i)
2358 {
2359 _httpPreflightHeaders.push_back(*i);
2360 }
2361 }
2362
2363 void setDisconnected()
2364 {
2365 {
2366 Lock<Mutex> l(_lock);
2367 if (_connected)
2368 {
2369 AMPS_CALL_EXCEPTION_WRAPPER(broadcastConnectionStateChanged(ConnectionStateListener::Disconnected));
2370 }
2371 _connected = false;
2372 _heartbeatTimer.setTimeout(0.0);
2373 // AC-1189 AC-1329 AC-1337 We need acks cleared while lock is held,
2374 // but not for unsent commands.
2375 clearAcks(UINT_MAX-1);
2376 }
2377 amps_client_disconnect(_client);
2378 _routes.clear();
2379 }
2380
2381 virtual void disconnect()
2382 {
2383 AMPS_CALL_EXCEPTION_WRAPPER(flushAcks());
2384 setDisconnected();
2385 // Abandon all acks, sent and unsent
2386 clearAcks(UINT_MAX);
2387 AMPS_CALL_EXCEPTION_WRAPPER(processDeferredExecutions());
2388 Lock<Mutex> l(_lock);
2389 broadcastConnectionStateChanged(ConnectionStateListener::Shutdown);
2390 }
2391
2392 void clearAcks(unsigned failedVersion)
2393 {
2394 // Have to lock to prevent race conditions
2395 Lock<Mutex> guard(_ackMapLock);
2396 {
2397 // Go ahead and signal any waiters if they are around...
2398 std::vector<std::string> worklist;
2399 for (AckMap::iterator i = _ackMap.begin(), e = _ackMap.end(); i != e; ++i)
2400 {
2401 if (i->second.getConnectionVersion() <= failedVersion)
2402 {
2403 i->second.setAbandoned();
2404 worklist.push_back(i->first);
2405 }
2406 }
2407
2408 for (std::vector<std::string>::iterator j = worklist.begin(), e = worklist.end(); j != e; ++j)
2409 {
2410 _ackMap.erase(*j);
2411 }
2412 }
2413
2414 _lock.signalAll();
2415 }
2416
2417 unsigned send(const Message& message)
2418 {
2419 Lock<Mutex> l(_lock);
2420 return _send(message);
2421 }
2422
2423 void sendWithoutRetry(const Message& message_)
2424 {
2425 Lock<Mutex> l(_lock);
2426 // If we got here while logon was in progress, then we tried to send
2427 // while we were disconnected so throw DisconnectedException
2428 if (_logonInProgress)
2429 {
2430 throw DisconnectedException("The client has been disconnected.");
2431 }
2432 _sendWithoutRetry(message_);
2433 }
2434
2435 void _sendWithoutRetry(const Message& message_)
2436 {
2437 amps_result result = amps_client_send(_client, message_.getMessage());
2438 if (result != AMPS_E_OK)
2439 {
2440 AMPSException::throwFor(_client, result);
2441 }
2442 }
2443
2444 // This version of send is used primarily by publish messages and anything
2445 // in a Store.
2446 unsigned _sendSequenced(const Message& message, amps_uint64_t haSeq, int isBatch_)
2447 {
2448 // Lock is already acquired
2449 amps_result result = AMPS_E_RETRY;
2450
2451 // Create a local reference to this message, as we'll need to hold on
2452 // to a reference to it in case reconnect occurs.
2453 Message localMessage = message;
2454 unsigned version = 0;
2455 bool isNotLogon = (haSeq) || (localMessage.getCommand().data()[0] != 'l');
2456
2457 while (result == AMPS_E_RETRY)
2458 {
2459 if (_logonInProgress && isNotLogon)
2460 {
2461 if (!_isRetryOnDisconnect)
2462 {
2463 if (haSeq)
2464 {
2465 // Prefer to return UINT_MAX to 0 so syncAckProcessing won't throw
2466 return UINT_MAX;
2467 }
2468 else
2469 {
2470 // retrySend is disabled so throw the error
2471 AMPSException::throwFor(_client, result);
2472 }
2473 }
2474 if (localMessage.getMessage() == message.getMessage())
2475 {
2476 localMessage = message.deepCopy();
2477 }
2478 while (_logonInProgress)
2479 {
2480 if (!_lock.wait(1000))
2481 {
2482 amps_invoke_waiting_function();
2483 }
2484 }
2485 }
2486 if (haSeq)
2487 {
2488 // Path for PublishStore messages
2489 if (haSeq <= _lastSentHaSequenceNumber)
2490 {
2491 // Prefer to return UINT_MAX to 0 so syncAckProcessing won't throw
2492 return UINT_MAX;
2493 }
2494 // It's possible to get here out of order, but this way we'll
2495 // always send in order.
2496 while (haSeq > _lastSentHaSequenceNumber + 1)
2497 {
2498 try
2499 {
2500 // Replayer updates _lastSentHaSsequenceNumber
2501 if (!_publishStore.replaySingle(_replayer,
2502 _lastSentHaSequenceNumber + 1))
2503 {
2504 continue;
2505 }
2506 result = AMPS_E_OK;
2507 version = _replayer._version;
2508 }
2509 #ifdef _WIN32
2510 catch (const DisconnectedException&)
2511 #else
2512 catch (const DisconnectedException& e)
2513 #endif
2514 {
2515 if (!_isRetryOnDisconnect)
2516 {
2517 // Prefer to return UINT_MAX to 0 so syncAckProcessing won't throw
2518 return UINT_MAX;
2519 }
2520 result = _replayer._res;
2521 break;
2522 }
2523 }
2524 ++_lastSentHaSequenceNumber;
2525 }
2526 result = amps_client_send_batch(_client,
2527 localMessage.getMessage(),
2528 &version,
2529 isBatch_);
2530 if (result != AMPS_E_OK)
2531 {
2532 if (!haSeq
2533 && localMessage.getMessage() == message.getMessage())
2534 {
2535 localMessage = message.deepCopy();
2536 }
2537 if (_isRetryOnDisconnect)
2538 {
2539 Unlock<Mutex> u(_lock);
2540 result = amps_client_attempt_reconnect(_client, version);
2541 // If this is in the publish store, it was
2542 // stored first and will have already been replayed by the
2543 // store after reconnect, so just return.
2544 if (haSeq
2545 && result == AMPS_E_RETRY)
2546 {
2547 // Prefer to return UINT_MAX to 0 so syncAckProcessing won't throw
2548 return UINT_MAX;
2549 }
2550 }
2551 else
2552 {
2553 // we don't throw if the message is in the publish store
2554 if (haSeq)
2555 {
2556 // Prefer to return UINT_MAX to 0 so syncAckProcessing won't throw
2557 return UINT_MAX;
2558 }
2559 // retrySend is disabled so throw the error
2560 // from the send as an exception, do not retry.
2561 AMPSException::throwFor(_client, result);
2562 }
2563 }
2564 if (result == AMPS_E_RETRY)
2565 {
2566 amps_invoke_waiting_function();
2567 }
2568 }
2569
2570 if (result != AMPS_E_OK)
2571 {
2572 // We should never get here for messages in the publish store
2573 AMPSException::throwFor(_client, result);
2574 }
2575 return version;
2576 }
2577
2578 // This version of send is used primarily by messages requesting acks
2579 unsigned _send(const Message& message, AMPS_ATOMIC_BASE_TYPE subscribeGeneration_ = 0)
2580 {
2581 // Lock is already acquired
2582 amps_result result = AMPS_E_RETRY;
2583
2584 // Create a local reference to this message, as we'll need to hold on
2585 // to a reference to it in case reconnect occurs.
2586 Message localMessage = message;
2587 unsigned version = 0;
2588 bool isHASubscribe = subscribeGeneration_ > 0;
2589
2590 while (result == AMPS_E_RETRY)
2591 {
2592 if (_logonInProgress && localMessage.getCommand().data()[0] != 'l')
2593 {
2594 if (!isHASubscribe
2595 && localMessage.getMessage() == message.getMessage())
2596 {
2597 localMessage = message.deepCopy();
2598 }
2599 while (_logonInProgress)
2600 {
2601 if (!_lock.wait(1000))
2602 {
2603 amps_invoke_waiting_function();
2604 if (!_isRetryOnDisconnect)
2605 {
2606 // retrySend is disabled so throw the error
2607 AMPSException::throwFor(_client, result);
2608 }
2609 }
2610 }
2611 }
2612 if (isHASubscribe
2613 && subscribeGeneration_ <= (AMPS_ATOMIC_BASE_TYPE)_subscriptionManagerGenerationCount)
2614 {
2615 // We return UINT_MAX because this is in the sub manager and will be sent
2616 // If we return 0, that will throw DisconnectedException and call
2617 // unsubscribe to remove it from sub manager and message routes.
2618 return UINT_MAX;
2619 }
2620 result = amps_client_send_batch(_client,
2621 localMessage.getMessage(),
2622 &version,
2623 0);
2624 if (result != AMPS_E_OK)
2625 {
2626 if (!isHASubscribe
2627 && localMessage.getMessage() == message.getMessage())
2628 {
2629 localMessage = message.deepCopy();
2630 }
2631 if (_isRetryOnDisconnect)
2632 {
2633 Unlock<Mutex> u(_lock);
2634 result = amps_client_attempt_reconnect(_client, version);
2635 // If this is an HA subscribe command, it was
2636 // stored first and will have already been replayed by the
2637 // store or sub manager after reconnect, so just return.
2638 if (isHASubscribe &&
2639 result == AMPS_E_RETRY)
2640 {
2641 // We return UINT_MAX because this is in the sub manager and will be sent
2642 // If we return 0, that will throw DisconnectedException and call
2643 // unsubscribe to remove it from sub manager and message routes.
2644 return UINT_MAX;
2645 }
2646 }
2647 else
2648 {
2649 // retrySend is disabled so throw the error
2650 // from the send as an exception, do not retry.
2651 AMPSException::throwFor(_client, result);
2652 }
2653 }
2654 if (result == AMPS_E_RETRY)
2655 {
2656 amps_invoke_waiting_function();
2657 }
2658 }
2659
2660 if (result != AMPS_E_OK)
2661 {
2662 AMPSException::throwFor(_client, result);
2663 }
2664 return version;
2665 }
2666
2667 void addMessageHandler(const Field& commandId_,
2668 const AMPS::MessageHandler& messageHandler_,
2669 unsigned requestedAcks_, Message::Command::Type commandType_)
2670 {
2671 Lock<Mutex> lock(_lock);
2672 _routes.addRoute(commandId_, messageHandler_, requestedAcks_,
2673 0, commandType_);
2674 }
2675
2676 bool removeMessageHandler(const Field& commandId_)
2677 {
2678 Lock<Mutex> lock(_lock);
2679 return _routes.removeRoute(commandId_);
2680 }
2681
2682 std::string send(const MessageHandler& messageHandler_,
2683 Message& message_,
2684 int timeout_ = 0,
2685 AMPS_ATOMIC_BASE_TYPE subscribeGeneration_ = 0)
2686 {
2687 Field id = message_.getCommandId();
2688 Field subId = message_.getSubscriptionId();
2689 Field qid = message_.getQueryId();
2690 bool isSubscribeOnly = false;
2691 bool replace = false;
2692 unsigned requestedAcks = message_.getAckTypeEnum();
2693 unsigned systemAddedAcks = Message::AckType::None;
2694 Message::Command::Type commandType = message_.getCommandEnum();
2695
2696 switch (commandType)
2697 {
2698 case Message::Command::Subscribe:
2699 case Message::Command::DeltaSubscribe:
2700 replace = message_.getOptions().operator std::string().find(AMPS_OPTIONS_REPLACE, 0, strlen(AMPS_OPTIONS_REPLACE) - 1) != std::string::npos;
2701 isSubscribeOnly = true;
2702 // fall through
2703 case Message::Command::SOWAndSubscribe:
2704 case Message::Command::SOWAndDeltaSubscribe:
2705 if (id.empty())
2706 {
2707 id = message_.newCommandId().getCommandId();
2708 }
2709 else
2710 {
2711 while (!replace && id != subId && _routes.hasRoute(id))
2712 {
2713 id = message_.newCommandId().getCommandId();
2714 }
2715 }
2716 if (subId.empty())
2717 {
2718 message_.setSubscriptionId(id);
2719 subId = id;
2720 }
2721 if (!message_.getBookmark().empty() && _bookmarkStore.isValid())
2722 {
2723 systemAddedAcks |= Message::AckType::Persisted;
2724 }
2725 // fall through
2726 case Message::Command::SOW:
2727 if (id.empty())
2728 {
2729 id = message_.newCommandId().getCommandId();
2730 }
2731 else
2732 {
2733 while (!replace && id != subId && _routes.hasRoute(id))
2734 {
2735 message_.newCommandId();
2736 if (qid == id)
2737 {
2738 qid = message_.getCommandId();
2739 message_.setQueryId(qid);
2740 }
2741 id = message_.getCommandId();
2742 }
2743 }
2744 if (!isSubscribeOnly)
2745 {
2746 if (qid.empty())
2747 {
2748 message_.setQueryID(id);
2749 qid = id;
2750 }
2751 else
2752 {
2753 while (!replace && qid != subId && qid != id
2754 && _routes.hasRoute(qid))
2755 {
2756 qid = message_.newQueryId().getQueryId();
2757 }
2758 }
2759 }
2760 systemAddedAcks |= Message::AckType::Processed;
2761 message_.setAckTypeEnum(requestedAcks | systemAddedAcks);
2762 {
2763 int routesAdded = 0;
2764 Lock<Mutex> l(_lock);
2765 // We can't check this until we have the lock
2766 if (subscribeGeneration_ > 0
2767 && subscribeGeneration_ <= _subscriptionManagerGenerationCount)
2768 {
2769 message_.setAckTypeEnum(requestedAcks);
2770 return std::string();
2771 }
2772 if (!subId.empty() && messageHandler_.isValid())
2773 {
2774 if (!_routes.hasRoute(subId))
2775 {
2776 ++routesAdded;
2777 }
2778 // This can replace a non-subscribe with a matching id
2779 // with a subscription but not another subscription.
2780 _routes.addRoute(subId, messageHandler_, requestedAcks,
2781 systemAddedAcks, commandType);
2782 }
2783 if (!isSubscribeOnly && !qid.empty()
2784 && messageHandler_.isValid() && qid != subId)
2785 {
2786 if (routesAdded == 0)
2787 {
2788 _routes.addRoute(qid, messageHandler_,
2789 requestedAcks, systemAddedAcks, commandType);
2790 }
2791 else
2792 {
2793 void* data = NULL;
2794 {
2795 Unlock<Mutex> u(_lock);
2796 data = amps_invoke_copy_route_function(
2797 messageHandler_.userData());
2798 }
2799 if (!data)
2800 {
2801 _routes.addRoute(qid, messageHandler_, requestedAcks,
2802 systemAddedAcks, commandType);
2803 }
2804 else
2805 {
2806 _routes.addRoute(qid,
2807 MessageHandler(messageHandler_.function(),
2808 data),
2809 requestedAcks, systemAddedAcks, commandType);
2810 }
2811 }
2812 ++routesAdded;
2813 }
2814 if (!id.empty() && messageHandler_.isValid()
2815 && requestedAcks & ~Message::AckType::Persisted
2816 && id != subId && id != qid)
2817 {
2818 if (routesAdded == 0)
2819 {
2820 _routes.addRoute(id, messageHandler_, requestedAcks,
2821 systemAddedAcks, commandType);
2822 }
2823 else
2824 {
2825 void* data = NULL;
2826 {
2827 Unlock<Mutex> u(_lock);
2828 data = amps_invoke_copy_route_function(
2829 messageHandler_.userData());
2830 }
2831 if (!data)
2832 {
2833 _routes.addRoute(id, messageHandler_, requestedAcks,
2834 systemAddedAcks, commandType);
2835 }
2836 else
2837 {
2838 _routes.addRoute(id,
2839 MessageHandler(messageHandler_.function(),
2840 data),
2841 requestedAcks,
2842 systemAddedAcks, commandType);
2843 }
2844 }
2845 ++routesAdded;
2846 }
2847 try
2848 {
2849 AckResponse resp = syncAckProcessing(timeout_, message_, 0, subscribeGeneration_);
2850 message_.setAckTypeEnum(requestedAcks);
2851 if (resp.getConnectionVersion() == UINT_MAX)
2852 {
2853 // Special case where message wasn't sent
2854 // Don't remove subId/qid routes as next try may have already
2855 // registered since an unlock was possible, but we can
2856 // remove cid if unique, as it could be used for acks.
2857 if (id != subId
2858 && id != qid)
2859 {
2860 _routes.removeRoute(id);
2861 }
2862 }
2863 }
2864 catch (...)
2865 {
2866 _routes.removeRoute(message_.getQueryID());
2867 _routes.removeRoute(message_.getSubscriptionId());
2868 _routes.removeRoute(id);
2869 message_.setAckTypeEnum(requestedAcks);
2870 throw;
2871 }
2872 }
2873 break;
2874 // These are valid commands that are used as-is
2875 case Message::Command::Unsubscribe:
2876 case Message::Command::Heartbeat:
2877 case Message::Command::Logon:
2878 case Message::Command::StartTimer:
2879 case Message::Command::StopTimer:
2880 case Message::Command::SOWDelete:
2881 {
2882 Lock<Mutex> l(_lock);
2883 // if an ack is requested, it'll need a command ID.
2884 if (message_.getAckTypeEnum() != Message::AckType::None)
2885 {
2886 if (id.empty())
2887 {
2888 message_.newCommandId();
2889 id = message_.getCommandId();
2890 }
2891 if (messageHandler_.isValid())
2892 {
2893 _routes.addRoute(id, messageHandler_, requestedAcks,
2894 Message::AckType::None, commandType);
2895 }
2896 }
2897 _send(message_);
2898 }
2899 break;
2900 case Message::Command::DeltaPublish:
2901 case Message::Command::Publish:
2902 {
2903 bool useSync = message_.getFilter().len() > 0;
2904 Lock<Mutex> l(_lock);
2905 // if an ack is requested, it'll need a command ID.
2906 unsigned ackType = message_.getAckTypeEnum();
2907 if (ackType != Message::AckType::None
2908 || useSync)
2909 {
2910 if (id.empty())
2911 {
2912 message_.newCommandId();
2913 id = message_.getCommandId();
2914 }
2915 if (messageHandler_.isValid())
2916 {
2917 _routes.addRoute(id, messageHandler_, requestedAcks,
2918 Message::AckType::None, commandType);
2919 }
2920 }
2921 if (useSync)
2922 {
2923 message_.setAckTypeEnum(ackType | Message::AckType::Processed);
2924 syncAckProcessing(timeout_, message_, 0, 0);
2925 }
2926 else
2927 {
2928 _sendSequenced(message_, 0, 1);
2929 }
2930 }
2931 break;
2932 // These are things that shouldn't be sent (not meaningful)
2933 case Message::Command::GroupBegin:
2934 case Message::Command::GroupEnd:
2935 case Message::Command::OOF:
2936 case Message::Command::Ack:
2937 case Message::Command::Unknown:
2938 default:
2939 throw CommandException("Command type " + message_.getCommand() + " can not be sent directly to AMPS");
2940 }
2941 message_.setAckTypeEnum(requestedAcks);
2942 return id;
2943 }
2944
2945 void setDisconnectHandler(const DisconnectHandler& disconnectHandler)
2946 {
2947 Lock<Mutex> l(_lock);
2948 _disconnectHandler = disconnectHandler;
2949 }
2950
2951 void setGlobalCommandTypeMessageHandler(const std::string& command_, const MessageHandler& handler_)
2952 {
2953 switch (command_[0])
2954 {
2955 #if 0 // Not currently implemented to avoid an extra branch in delivery
2956 case 'p':
2957 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::Publish] = handler_;
2958 break;
2959 case 's':
2960 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::SOW] = handler_;
2961 break;
2962 #endif
2963 case 'h':
2964 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::Heartbeat] = handler_;
2965 break;
2966 #if 0 // Not currently implemented to avoid an extra branch in delivery
2967 case 'g':
2968 if (command_[6] == 'b')
2969 {
2970 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::GroupBegin] = handler_;
2971 }
2972 else if (command_[6] == 'e')
2973 {
2974 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::GroupEnd] = handler_;
2975 }
2976 else
2977 {
2978 std::ostringstream os;
2979 os << "Invalid command '" << command_ << "' passed to setGlobalCommandTypeHandler";
2980 throw CommandException(os.str());
2981 }
2982 break;
2983 case 'o':
2984 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::OOF] = handler_;
2985 break;
2986 #endif
2987 case 'a':
2988 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::Ack] = handler_;
2989 break;
2990 case 'l':
2991 case 'L':
2992 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::LastChance] = handler_;
2993 break;
2994 case 'd':
2995 case 'D':
2996 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::DuplicateMessage] = handler_;
2997 break;
2998 default:
2999 std::ostringstream os;
3000 os << "Invalid command '" << command_ << "' passed to setGlobalCommandTypeHandler";
3001 throw CommandException(os.str());
3002 break;
3003 }
3004 }
3005
3006 void setGlobalCommandTypeMessageHandler(const Message::Command::Type command_, const MessageHandler& handler_)
3007 {
3008 switch (command_)
3009 {
3010 #if 0 // Not currently implemented to avoid an extra branch in delivery
3011 case Message::Command::Publish:
3012 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::Publish] = handler_;
3013 break;
3014 case Message::Command::SOW:
3015 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::SOW] = handler_;
3016 break;
3017 #endif
3018 case Message::Command::Heartbeat:
3019 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::Heartbeat] = handler_;
3020 break;
3021 #if 0 // Not currently implemented to avoid an extra branch in delivery
3022 case Message::Command::GroupBegin:
3023 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::GroupBegin] = handler_;
3024 break;
3025 case Message::Command::GroupEnd:
3026 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::GroupEnd] = handler_;
3027 break;
3028 case Message::Command::OOF:
3029 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::OOF] = handler_;
3030 break;
3031 #endif
3032 case Message::Command::Ack:
3033 _globalCommandTypeHandlers[GlobalCommandTypeHandlers::Ack] = handler_;
3034 break;
3035 default:
3036 unsigned bits = 0;
3037 unsigned command = command_;
3038 while (command > 0)
3039 {
3040 ++bits;
3041 command >>= 1;
3042 }
3043 char errBuf[128];
3044 AMPS_snprintf(errBuf, sizeof(errBuf),
3045 "Invalid command '%.*s' passed to setGlobalCommandTypeHandler",
3046 CommandConstants<0>::Lengths[bits],
3047 CommandConstants<0>::Values[bits]);
3048 throw CommandException(errBuf);
3049 break;
3050 }
3051 }
3052
3053 void setGlobalCommandTypeMessageHandler(const GlobalCommandTypeHandlers handlerType_, const MessageHandler& handler_)
3054 {
3055 _globalCommandTypeHandlers[handlerType_] = handler_;
3056 }
3057
3058 void setFailedWriteHandler(FailedWriteHandler* handler_)
3059 {
3060 Lock<Mutex> l(_lock);
3061 _failedWriteHandler.reset(handler_);
3062 }
3063
3064 void setPublishStore(const Store& publishStore_)
3065 {
3066 Lock<Mutex> l(_lock);
3067 if (_connected)
3068 {
3069 throw AlreadyConnectedException("Setting a publish store on a connected client is undefined behavior");
3070 }
3071 _publishStore = publishStore_;
3072 }
3073
3074 void setBookmarkStore(const BookmarkStore& bookmarkStore_)
3075 {
3076 Lock<Mutex> l(_lock);
3077 if (_connected)
3078 {
3079 throw AlreadyConnectedException("Setting a bookmark store on a connected client is undefined behavior");
3080 }
3081 _bookmarkStore = bookmarkStore_;
3082 }
3083
3084 void setSubscriptionManager(SubscriptionManager* subscriptionManager_)
3085 {
3086 Lock<Mutex> l(_lock);
3087 if (_connected)
3088 {
3089 throw AlreadyConnectedException("Setting a subscription manager on a connected client is undefined behavior");
3090 }
3091 _subscriptionManager.reset(subscriptionManager_);
3092 captureSubscriptionManagerGenerationCount();
3093 }
3094
3095 SubscriptionManager* getSubscriptionManager() const
3096 {
3097 return const_cast<SubscriptionManager*>(_subscriptionManager.get());
3098 }
3099
3100 DisconnectHandler getDisconnectHandler() const
3101 {
3102 return _disconnectHandler;
3103 }
3104
3105 MessageHandler getDuplicateMessageHandler() const
3106 {
3107 return _globalCommandTypeHandlers[GlobalCommandTypeHandlers::DuplicateMessage];
3108 }
3109
3110 FailedWriteHandler* getFailedWriteHandler() const
3111 {
3112 return const_cast<FailedWriteHandler*>(_failedWriteHandler.get());
3113 }
3114
3115 Store getPublishStore() const
3116 {
3117 return _publishStore;
3118 }
3119
3120 BookmarkStore getBookmarkStore() const
3121 {
3122 return _bookmarkStore;
3123 }
3124
3125 amps_uint64_t publish(Message& message_)
3126 {
3127 if (!_publishStore.isValid())
3128 {
3129 Lock<Mutex> l(_lock);
3130 _sendSequenced(message_, 0, 1);
3131 return 0;
3132 }
3133 else
3134 {
3135 message_.setAckTypeEnum(message_.getAckTypeEnum() | Message::AckType::Persisted);
3136 amps_uint64_t haSequenceNumber = _publishStore.store(message_);
3137 char buf[AMPS_NUMBER_BUFFER_LEN];
3138 size_t pos = convertToCharArray(buf, haSequenceNumber);
3139 message_.assignSequence(buf + pos, AMPS_NUMBER_BUFFER_LEN - pos);
3140 {
3141 Lock<Mutex> l(_lock);
3142 _sendSequenced(message_, haSequenceNumber, 1);
3143 }
3144 return haSequenceNumber;
3145 }
3146 }
3147
3148 amps_uint64_t publish(const char* topic_, size_t topicLen_, const char* data_, size_t dataLen_)
3149 {
3150 threadLocalMessage.reset();
3151 threadLocalMessage.setCommandEnum(Message::Command::Publish)
3152 .assignTopic(topic_, topicLen_)
3153 .assignData(data_, dataLen_);
3154 if (!_publishStore.isValid())
3155 {
3156 Lock<Mutex> l(_lock);
3157 _sendSequenced(threadLocalMessage, 0, 1);
3158 return 0;
3159 }
3160 else
3161 {
3162 return _publish();
3163 }
3164 }
3165
3166 amps_uint64_t publish(const char* topic_, size_t topicLen_, const char* data_,
3167 size_t dataLen_, unsigned long expiration_)
3168 {
3169 threadLocalMessage.reset();
3170 char exprBuf[AMPS_NUMBER_BUFFER_LEN];
3171 size_t exprPos = convertToCharArray(exprBuf, expiration_);
3172 threadLocalMessage.setCommandEnum(Message::Command::Publish)
3173 .assignTopic(topic_, topicLen_)
3174 .assignData(data_, dataLen_)
3175 .assignExpiration(exprBuf + exprPos,
3176 AMPS_NUMBER_BUFFER_LEN - exprPos);
3177 if (!_publishStore.isValid())
3178 {
3179 Lock<Mutex> l(_lock);
3180 _sendSequenced(threadLocalMessage, 0, 1);
3181 return 0;
3182 }
3183 else
3184 {
3185 return _publish();
3186 }
3187 }
3188
3189 amps_uint64_t publish(const char* topic_, size_t topicLen_,
3190 const char* data_, size_t dataLen_,
3191 const char* correlationId_, size_t correlationIdLen_)
3192 {
3193 threadLocalMessage.reset();
3194 threadLocalMessage.setCommandEnum(Message::Command::Publish)
3195 .assignTopic(topic_, topicLen_)
3196 .assignData(data_, dataLen_)
3197 .assignCorrelationId(correlationId_, correlationIdLen_);
3198 if (!_publishStore.isValid())
3199 {
3200 Lock<Mutex> l(_lock);
3201 _sendSequenced(threadLocalMessage, 0, 1);
3202 return 0;
3203 }
3204 else
3205 {
3206 return _publish();
3207 }
3208 }
3209
3210 amps_uint64_t publish(const char* topic_, size_t topicLen_,
3211 const char* data_, size_t dataLen_,
3212 const char* correlationId_, size_t correlationIdLen_,
3213 unsigned long expiration_)
3214 {
3215 threadLocalMessage.reset();
3216 char exprBuf[AMPS_NUMBER_BUFFER_LEN];
3217 size_t exprPos = convertToCharArray(exprBuf, expiration_);
3218 threadLocalMessage.setCommandEnum(Message::Command::Publish)
3219 .assignTopic(topic_, topicLen_)
3220 .assignData(data_, dataLen_)
3221 .assignCorrelationId(correlationId_, correlationIdLen_)
3222 .assignExpiration(exprBuf + exprPos,
3223 AMPS_NUMBER_BUFFER_LEN - exprPos);
3224 if (!_publishStore.isValid())
3225 {
3226 Lock<Mutex> l(_lock);
3227 _sendSequenced(threadLocalMessage, 0, 1);
3228 return 0;
3229 }
3230 else
3231 {
3232 return _publish();
3233 }
3234 }
3235
3236 amps_uint64_t publishKeyed(const char* topic_, size_t topicLen_,
3237 const char* data_, size_t dataLen_,
3238 const char* sowKey_, size_t sowKeyLen_)
3239 {
3240 threadLocalMessage.reset();
3241 threadLocalMessage.setCommandEnum(Message::Command::Publish)
3242 .assignTopic(topic_, topicLen_)
3243 .assignData(data_, dataLen_)
3244 .assignSowKey(sowKey_, sowKeyLen_);
3245 if (!_publishStore.isValid())
3246 {
3247 Lock<Mutex> l(_lock);
3248 _sendSequenced(threadLocalMessage, 0, 1);
3249 return 0;
3250 }
3251 else
3252 {
3253 return _publish();
3254 }
3255 }
3256
3257 amps_uint64_t publishKeyed(const char* topic_, size_t topicLen_,
3258 const char* data_, size_t dataLen_,
3259 const char* sowKey_, size_t sowKeyLen_,
3260 unsigned long expiration_)
3261 {
3262 threadLocalMessage.reset();
3263 char exprBuf[AMPS_NUMBER_BUFFER_LEN];
3264 size_t exprPos = convertToCharArray(exprBuf, expiration_);
3265 threadLocalMessage.setCommandEnum(Message::Command::Publish)
3266 .assignTopic(topic_, topicLen_)
3267 .assignData(data_, dataLen_)
3268 .assignSowKey(sowKey_, sowKeyLen_)
3269 .assignExpiration(exprBuf + exprPos,
3270 AMPS_NUMBER_BUFFER_LEN - exprPos);
3271 if (!_publishStore.isValid())
3272 {
3273 Lock<Mutex> l(_lock);
3274 _sendSequenced(threadLocalMessage, 0, 1);
3275 return 0;
3276 }
3277 else
3278 {
3279 return _publish();
3280 }
3281 }
3282
3283 amps_uint64_t publish(const char* topic_, size_t topicLen_,
3284 const char* data_, size_t dataLen_,
3285 const char* correlationId_, size_t correlationIdLen_,
3286 const char* sowKey_, size_t sowKeyLen_)
3287 {
3288 threadLocalMessage.reset();
3289 threadLocalMessage.setCommandEnum(Message::Command::Publish)
3290 .assignTopic(topic_, topicLen_)
3291 .assignData(data_, dataLen_)
3292 .assignCorrelationId(correlationId_, correlationIdLen_)
3293 .assignSowKey(sowKey_, sowKeyLen_);
3294 if (!_publishStore.isValid())
3295 {
3296 Lock<Mutex> l(_lock);
3297 _sendSequenced(threadLocalMessage, 0, 1);
3298 return 0;
3299 }
3300 else
3301 {
3302 return _publish();
3303 }
3304 }
3305
3306 amps_uint64_t publish(const char* topic_, size_t topicLen_,
3307 const char* data_, size_t dataLen_,
3308 const char* correlationId_, size_t correlationIdLen_,
3309 const char* sowKey_, size_t sowKeyLen_,
3310 unsigned long expiration_)
3311 {
3312 threadLocalMessage.reset();
3313 char exprBuf[AMPS_NUMBER_BUFFER_LEN];
3314 size_t exprPos = convertToCharArray(exprBuf, expiration_);
3315 threadLocalMessage.setCommandEnum(Message::Command::Publish)
3316 .assignTopic(topic_, topicLen_)
3317 .assignData(data_, dataLen_)
3318 .assignCorrelationId(correlationId_, correlationIdLen_)
3319 .assignSowKey(sowKey_, sowKeyLen_)
3320 .assignExpiration(exprBuf + exprPos,
3321 AMPS_NUMBER_BUFFER_LEN - exprPos);
3322 if (!_publishStore.isValid())
3323 {
3324 Lock<Mutex> l(_lock);
3325 _sendSequenced(threadLocalMessage, 0, 1);
3326 return 0;
3327 }
3328 else
3329 {
3330 return _publish();
3331 }
3332 }
3333
3334 class FlushAckHandler : ConnectionStateListener
3335 {
3336 private:
3337 ClientImpl* _pClient;
3338 Field _cmdId;
3339 #if __cplusplus >= 201100L || _MSC_VER >= 1900
3340 std::atomic<bool> _acked;
3341 std::atomic<bool> _disconnected;
3342 #else
3343 volatile bool _acked;
3344 volatile bool _disconnected;
3345 #endif
3346 public:
3347 FlushAckHandler(ClientImpl* pClient_)
3348 : _pClient(pClient_), _cmdId(), _acked(false), _disconnected(false)
3349 {
3350 pClient_->addConnectionStateListener(this);
3351 }
3352 ~FlushAckHandler()
3353 {
3354 _pClient->removeConnectionStateListener(this);
3355 _pClient->removeMessageHandler(_cmdId);
3356 _cmdId.clear();
3357 }
3358 void setCommandId(const Field& cmdId_)
3359 {
3360 _cmdId.deepCopy(cmdId_);
3361 }
3362 void invoke(const Message&)
3363 {
3364 _acked = true;
3365 }
3366 void connectionStateChanged(State state_)
3367 {
3368 if (state_ <= Shutdown)
3369 {
3370 _disconnected = true;
3371 }
3372 }
3373 bool acked()
3374 {
3375 return _acked;
3376 }
3377 bool done()
3378 {
3379 return _acked || _disconnected;
3380 }
3381 };
3382
3383 void publishFlush(long timeout_, unsigned ackType_)
3384 {
3385 static const char* processed = "processed"; // -V1096
3386 static const size_t processedLen = strlen(processed); // -V1096
3387 static const char* persisted = "persisted"; // -V1096
3388 static const size_t persistedLen = strlen(persisted); // -V1096
3389 static const char* flush = "flush"; // -V1096
3390 static const size_t flushLen = strlen(flush); // -V1096
3391 static const VersionInfo minPersisted("5.3.3.0"); // -V1096
3392 static const VersionInfo minFlush("4"); // -V1096
3393 if (ackType_ != Message::AckType::Processed
3394 && ackType_ != Message::AckType::Persisted)
3395 {
3396 throw CommandException("Flush can only be used with processed or persisted acks.");
3397 }
3398 FlushAckHandler flushHandler(this);
3399 if (_serverVersion >= minFlush)
3400 {
3401 Lock<Mutex> l(_lock);
3402 if (!_connected)
3403 {
3404 throw DisconnectedException("Not connected trying to flush");
3405 }
3406 threadLocalMessage.reset();
3407 threadLocalMessage.newCommandId();
3408 threadLocalMessage.assignCommand(flush, flushLen);
3409 if (_serverVersion < minPersisted
3410 || ackType_ == Message::AckType::Processed)
3411 {
3412 threadLocalMessage.assignAckType(processed, processedLen);
3413 }
3414 else
3415 {
3416 threadLocalMessage.assignAckType(persisted, persistedLen);
3417 }
3418 flushHandler.setCommandId(threadLocalMessage.getCommandId());
3419 addMessageHandler(threadLocalMessage.getCommandId(),
3420 std::bind(&FlushAckHandler::invoke,
3421 std::ref(flushHandler),
3422 std::placeholders::_1),
3423 ackType_, threadLocalMessage.getCommandEnum());
3424 NoDelay noDelay(_client);
3425 _send(threadLocalMessage);
3426 }
3427 if (_publishStore.isValid())
3428 {
3429 try
3430 {
3431 _publishStore.flush(timeout_);
3432 }
3433 catch (const AMPSException& ex)
3434 {
3435 AMPS_UNHANDLED_EXCEPTION(ex);
3436 throw;
3437 }
3438 }
3439 else if (_serverVersion < minFlush)
3440 {
3441 if (timeout_ > 0)
3442 {
3443 AMPS_USLEEP(timeout_ * 1000);
3444 }
3445 else
3446 {
3447 AMPS_USLEEP(1000 * 1000);
3448 }
3449 return;
3450 }
3451 if (timeout_)
3452 {
3453 Timer timer((double)timeout_);
3454 timer.start();
3455 while (!timer.check() && !flushHandler.done())
3456 {
3457 AMPS_USLEEP(10000);
3458 amps_invoke_waiting_function();
3459 }
3460 }
3461 else
3462 {
3463 while (!flushHandler.done())
3464 {
3465 AMPS_USLEEP(10000);
3466 amps_invoke_waiting_function();
3467 }
3468 }
3469 // No response or disconnect in timeout interval
3470 if (!flushHandler.done())
3471 {
3472 throw TimedOutException("Timed out waiting for flush");
3473 }
3474 // We got disconnected and there is no publish store
3475 if (!flushHandler.acked() && !_publishStore.isValid())
3476 {
3477 throw DisconnectedException("Disconnected waiting for flush");
3478 }
3479 }
3480
3481 amps_uint64_t deltaPublish(const char* topic_, size_t topicLength_,
3482 const char* data_, size_t dataLength_)
3483 {
3484 threadLocalMessage.reset();
3485 threadLocalMessage.setCommandEnum(Message::Command::DeltaPublish)
3486 .assignTopic(topic_, topicLength_)
3487 .assignData(data_, dataLength_);
3488 if (!_publishStore.isValid())
3489 {
3490 Lock<Mutex> l(_lock);
3491 _sendSequenced(threadLocalMessage, 0, 1);
3492 return 0;
3493 }
3494 else
3495 {
3496 return _publish();
3497 }
3498 }
3499
3500 amps_uint64_t deltaPublish(const char* topic_, size_t topicLength_,
3501 const char* data_, size_t dataLength_,
3502 unsigned long expiration_)
3503 {
3504 threadLocalMessage.reset();
3505 char exprBuf[AMPS_NUMBER_BUFFER_LEN];
3506 size_t exprPos = convertToCharArray(exprBuf, expiration_);
3507 threadLocalMessage.setCommandEnum(Message::Command::DeltaPublish)
3508 .assignTopic(topic_, topicLength_)
3509 .assignData(data_, dataLength_)
3510 .assignExpiration(exprBuf + exprPos,
3511 AMPS_NUMBER_BUFFER_LEN - exprPos);
3512 if (!_publishStore.isValid())
3513 {
3514 Lock<Mutex> l(_lock);
3515 _sendSequenced(threadLocalMessage, 0, 1);
3516 return 0;
3517 }
3518 else
3519 {
3520 return _publish();
3521 }
3522 }
3523
3524 amps_uint64_t _publish(void)
3525 {
3526 threadLocalMessage.setAckTypeEnum(Message::AckType::Persisted);
3527 amps_uint64_t haSequenceNumber = _publishStore.store(threadLocalMessage);
3528 char buf[AMPS_NUMBER_BUFFER_LEN];
3529 size_t pos = convertToCharArray(buf, haSequenceNumber);
3530 threadLocalMessage.assignSequence(buf + pos, AMPS_NUMBER_BUFFER_LEN - pos);
3531 {
3532 Lock<Mutex> l(_lock);
3533 _sendSequenced(threadLocalMessage, haSequenceNumber, 1);
3534 }
3535 return haSequenceNumber;
3536 }
3537
3538 virtual std::string logon(long timeout_, Authenticator& authenticator_,
3539 const char* options_ = NULL);
3540
3541 virtual std::string _logon(long timeout_, Authenticator& authenticator_,
3542 const char* options_ = NULL)
3543 {
3544 // _lock is already acquired
3545 threadLocalMessage.reset();
3546 threadLocalMessage.newCommandId();
3547 std::string newCommandId = threadLocalMessage.getCommandId();
3548 threadLocalMessage.setCommandEnum(Message::Command::Logon);
3549 threadLocalMessage.setClientName(_name);
3550 #ifdef AMPS_CLIENT_VERSION_WITH_LANGUAGE
3551 threadLocalMessage.assignVersion(AMPS_CLIENT_VERSION_WITH_LANGUAGE,
3552 strlen(AMPS_CLIENT_VERSION_WITH_LANGUAGE));
3553 #endif
3554 URI uri(_lastUri);
3555 if (uri.user().size())
3556 {
3557 threadLocalMessage.setUserId(uri.user());
3558 }
3559 if (uri.password().size())
3560 {
3561 threadLocalMessage.setPassword(uri.password());
3562 }
3563 if (uri.protocol() == "amps" && uri.messageType().size())
3564 {
3565 threadLocalMessage.setMessageType(uri.messageType());
3566 }
3567 if (uri.isTrue("pretty"))
3568 {
3569 threadLocalMessage.setOptions("pretty");
3570 }
3571
3572 threadLocalMessage.setPassword(authenticator_.authenticate(threadLocalMessage.getUserId(), threadLocalMessage.getPassword()));
3573 if (!_logonCorrelationData.empty())
3574 {
3575 threadLocalMessage.assignCorrelationId(_logonCorrelationData);
3576 }
3577 if (options_)
3578 {
3579 threadLocalMessage.setOptions(options_);
3580 }
3581 _username = threadLocalMessage.getUserId();
3582 try
3583 {
3584 AtomicFlagFlip pubFlip(&_logonInProgress);
3585 NoDelay noDelay(_client);
3586 while (true)
3587 {
3588 threadLocalMessage.setAckTypeEnum(Message::AckType::Processed);
3589 AckResponse ack = syncAckProcessing(timeout_, threadLocalMessage);
3590 if (ack.status() == "retry")
3591 {
3592 threadLocalMessage.setPassword(authenticator_.retry(ack.username(), ack.password()));
3593 _username = ack.username();
3594 threadLocalMessage.setUserId(_username);
3595 }
3596 else
3597 {
3598 authenticator_.completed(ack.username(), ack.password(), ack.reason());
3599 break;
3600 }
3601 }
3602 broadcastConnectionStateChanged(ConnectionStateListener::LoggedOn);
3603
3604 // Now re-send the heartbeat command if configured
3605 _sendHeartbeat();
3606 // Signal any threads waiting for _logonInProgress
3607 _lock.signalAll();
3608 }
3609 catch (const AMPSException& ex)
3610 {
3611 {
3612 Unlock<Mutex> u(_lock);
3613 setDisconnected();
3614 }
3615 _lock.signalAll();
3616 AMPS_UNHANDLED_EXCEPTION(ex);
3617 throw;
3618 }
3619 catch (...)
3620 {
3621 {
3622 Unlock<Mutex> u(_lock);
3623 setDisconnected();
3624 }
3625 _lock.signalAll();
3626 throw;
3627 }
3628
3629 if (_publishStore.isValid())
3630 {
3631 try
3632 {
3633 _publishStore.replay(_replayer);
3634 broadcastConnectionStateChanged(ConnectionStateListener::PublishReplayed);
3635 }
3636 catch (const PublishStoreGapException& ex)
3637 {
3638 {
3639 Unlock<Mutex> u(_lock);
3640 setDisconnected();
3641 }
3642 _lock.signalAll();
3643 AMPS_UNHANDLED_EXCEPTION(ex);
3644 throw;
3645 }
3646 catch (const StoreException& ex)
3647 {
3648 {
3649 Unlock<Mutex> u(_lock);
3650 setDisconnected();
3651 }
3652 _lock.signalAll();
3653 std::ostringstream os;
3654 os << "A local store exception occurred while logging on."
3655 << ex.toString();
3656 throw ConnectionException(os.str());
3657 }
3658 catch (const AMPSException& ex)
3659 {
3660 {
3661 Unlock<Mutex> u(_lock);
3662 setDisconnected();
3663 }
3664 _lock.signalAll();
3665 AMPS_UNHANDLED_EXCEPTION(ex);
3666 throw;
3667 }
3668 catch (const std::exception& ex)
3669 {
3670 {
3671 Unlock<Mutex> u(_lock);
3672 setDisconnected();
3673 }
3674 _lock.signalAll();
3675 AMPS_UNHANDLED_EXCEPTION(ex);
3676 throw;
3677 }
3678 catch (...)
3679 {
3680 {
3681 Unlock<Mutex> u(_lock);
3682 setDisconnected();
3683 }
3684 _lock.signalAll();
3685 throw;
3686 }
3687 }
3688 _lock.signalAll();
3689 return newCommandId;
3690 }
3691
3692 std::string subscribe(const MessageHandler& messageHandler_,
3693 const std::string& topic_,
3694 long timeout_,
3695 const std::string& filter_,
3696 const std::string& bookmark_,
3697 const std::string& options_,
3698 const std::string& subId_,
3699 bool isHASubscribe_ = true)
3700 {
3701 isHASubscribe_ &= (bool)_subscriptionManager;
3702 Lock<Mutex> l(_lock);
3703 threadLocalMessage.reset();
3704 threadLocalMessage.setCommandEnum(Message::Command::Subscribe);
3705 threadLocalMessage.newCommandId();
3706 std::string subId(subId_);
3707 if (subId.empty())
3708 {
3709 if (options_.find(AMPS_OPTIONS_REPLACE, 0, strlen(AMPS_OPTIONS_REPLACE) - 1) != std::string::npos)
3710 {
3711 throw ConnectionException("Cannot issue a replacement subscription; a valid subscription id is required.");
3712 }
3713
3714 subId = threadLocalMessage.getCommandId();
3715 }
3716 threadLocalMessage.setSubscriptionId(subId);
3717 AMPS::Message::Field subIdField = threadLocalMessage.getSubscriptionId();
3718 unsigned ackTypes = Message::AckType::Processed;
3719
3720 if (!bookmark_.empty() && _bookmarkStore.isValid())
3721 {
3722 ackTypes |= Message::AckType::Persisted;
3723 }
3724 threadLocalMessage.setTopic(topic_);
3725
3726 if (filter_.length())
3727 {
3728 threadLocalMessage.setFilter(filter_);
3729 }
3730 if (bookmark_.length())
3731 {
3732 if (bookmark_ == AMPS_BOOKMARK_RECENT)
3733 {
3734 // The returned Field is a deep copy, so use assign to get it cleared
3735 Message::Field mostRecent = _bookmarkStore.getMostRecent(subIdField);
3736 threadLocalMessage.assignOwnershipBookmark(mostRecent);
3737 }
3738 else
3739 {
3740 threadLocalMessage.setBookmark(bookmark_);
3741 if (_bookmarkStore.isValid())
3742 {
3743 if (bookmark_ != AMPS_BOOKMARK_NOW &&
3744 bookmark_ != AMPS_BOOKMARK_EPOCH)
3745 {
3746 _bookmarkStore.log(threadLocalMessage);
3747 _bookmarkStore.discard(threadLocalMessage);
3748 _bookmarkStore.persisted(subIdField, threadLocalMessage.getBookmark());
3749 }
3750 }
3751 }
3752 }
3753 if (options_.length())
3754 {
3755 threadLocalMessage.setOptions(options_);
3756 }
3757
3758 Message message = threadLocalMessage;
3759 AMPS_ATOMIC_BASE_TYPE generation = (AMPS_ATOMIC_BASE_TYPE)0;
3760 if (isHASubscribe_)
3761 {
3762 message = threadLocalMessage.deepCopy();
3763 {
3764 Unlock<Mutex> u(_lock);
3765 generation =
3766 _subscriptionManager->subscribe(messageHandler_, message,
3767 Message::AckType::None);
3768 }
3769 if (generation <= (AMPS_ATOMIC_BASE_TYPE)_subscriptionManagerGenerationCount)
3770 {
3771 return subId;
3772 }
3773 }
3774 if (!_routes.hasRoute(message.getSubscriptionId()))
3775 {
3776 _routes.addRoute(message.getSubscriptionId(), messageHandler_,
3777 Message::AckType::None, ackTypes, message.getCommandEnum());
3778 }
3779 message.setAckTypeEnum(ackTypes);
3780 if (!options_.empty())
3781 {
3782 message.setOptions(options_);
3783 }
3784 try
3785 {
3786 syncAckProcessing(timeout_, message, 0, generation);
3787 }
3788 catch (const DisconnectedException&)
3789 {
3790 if (!isHASubscribe_)
3791 {
3792 _routes.removeRoute(subIdField);
3793 throw;
3794 }
3795 else
3796 {
3797 AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(subIdField));
3798 throw;
3799 }
3800 }
3801 catch (const TimedOutException&)
3802 {
3803 AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(subIdField));
3804 throw;
3805 }
3806 catch (...)
3807 {
3808 if (isHASubscribe_)
3809 {
3810 // Have to unlock before calling into sub manager to avoid deadlock
3811 Unlock<Mutex> unlock(_lock);
3812 _subscriptionManager->unsubscribe(subIdField);
3813 }
3814 _routes.removeRoute(subIdField);
3815 throw;
3816 }
3817
3818 return subId;
3819 }
3820 std::string deltaSubscribe(const MessageHandler& messageHandler_,
3821 const std::string& topic_,
3822 long timeout_,
3823 const std::string& filter_,
3824 const std::string& bookmark_,
3825 const std::string& options_,
3826 const std::string& subId_ = "",
3827 bool isHASubscribe_ = true)
3828 {
3829 isHASubscribe_ &= (bool)_subscriptionManager;
3830 Lock<Mutex> l(_lock);
3831 threadLocalMessage.reset();
3832 threadLocalMessage.setCommandEnum(Message::Command::DeltaSubscribe);
3833 threadLocalMessage.newCommandId();
3834 std::string subId(subId_);
3835 if (subId.empty())
3836 {
3837 subId = threadLocalMessage.getCommandId();
3838 }
3839 threadLocalMessage.setSubscriptionId(subId);
3840 AMPS::Message::Field subIdField = threadLocalMessage.getSubscriptionId();
3841 unsigned ackTypes = Message::AckType::Processed;
3842
3843 if (!bookmark_.empty() && _bookmarkStore.isValid())
3844 {
3845 ackTypes |= Message::AckType::Persisted;
3846 }
3847 threadLocalMessage.setTopic(topic_);
3848 if (filter_.length())
3849 {
3850 threadLocalMessage.setFilter(filter_);
3851 }
3852 if (bookmark_.length())
3853 {
3854 if (bookmark_ == AMPS_BOOKMARK_RECENT)
3855 {
3856 // The returned Field is a deep copy, so use assign to get it cleared
3857 Message::Field mostRecent = _bookmarkStore.getMostRecent(subIdField);
3858 threadLocalMessage.assignOwnershipBookmark(mostRecent);
3859 }
3860 else
3861 {
3862 threadLocalMessage.setBookmark(bookmark_);
3863 if (_bookmarkStore.isValid())
3864 {
3865 if (bookmark_ != AMPS_BOOKMARK_NOW &&
3866 bookmark_ != AMPS_BOOKMARK_EPOCH)
3867 {
3868 _bookmarkStore.log(threadLocalMessage);
3869 _bookmarkStore.discard(threadLocalMessage);
3870 _bookmarkStore.persisted(subIdField, threadLocalMessage.getBookmark());
3871 }
3872 }
3873 }
3874 }
3875 if (options_.length())
3876 {
3877 threadLocalMessage.setOptions(options_);
3878 }
3879 AMPS_ATOMIC_BASE_TYPE generation = (AMPS_ATOMIC_BASE_TYPE)0;
3880 if (isHASubscribe_)
3881 {
3882 {
3883 Unlock<Mutex> u(_lock);
3884 generation =
3885 _subscriptionManager->subscribe(messageHandler_, threadLocalMessage.deepCopy(),
3886 Message::AckType::None);
3887 }
3888 if (generation <= (AMPS_ATOMIC_BASE_TYPE)_subscriptionManagerGenerationCount)
3889 {
3890 return subId;
3891 }
3892 }
3893 if (!_routes.hasRoute(threadLocalMessage.getSubscriptionId()))
3894 {
3895 _routes.addRoute(threadLocalMessage.getSubscriptionId(), messageHandler_,
3896 Message::AckType::None, ackTypes, threadLocalMessage.getCommandEnum());
3897 }
3898 threadLocalMessage.setAckTypeEnum(ackTypes);
3899 if (!options_.empty())
3900 {
3901 threadLocalMessage.setOptions(options_);
3902 }
3903 try
3904 {
3905 syncAckProcessing(timeout_, threadLocalMessage, 0, generation);
3906 }
3907 catch (const DisconnectedException&)
3908 {
3909 if (!isHASubscribe_)
3910 {
3911 _routes.removeRoute(subIdField);
3912 throw;
3913 }
3914 }
3915 catch (const TimedOutException&)
3916 {
3917 AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(subIdField));
3918 throw;
3919 }
3920 catch (...)
3921 {
3922 if (isHASubscribe_)
3923 {
3924 // Have to unlock before calling into sub manager to avoid deadlock
3925 Unlock<Mutex> unlock(_lock);
3926 _subscriptionManager->unsubscribe(subIdField);
3927 }
3928 _routes.removeRoute(subIdField);
3929 throw;
3930 }
3931 return subId;
3932 }
3933
3934 void unsubscribe(const std::string& id)
3935 {
3936 Lock<Mutex> l(_lock);
3937 unsubscribeInternal(id);
3938 }
3939
3940 void unsubscribe(void)
3941 {
3942 if (_subscriptionManager)
3943 {
3944 _subscriptionManager->clear();
3945 }
3946 {
3947 _routes.unsubscribeAll();
3948 Lock<Mutex> l(_lock);
3949 threadLocalMessage.reset();
3950 threadLocalMessage.setCommandEnum(Message::Command::Unsubscribe);
3951 threadLocalMessage.newCommandId();
3952 threadLocalMessage.setSubscriptionId("all");
3953 _sendWithoutRetry(threadLocalMessage);
3954 }
3955 deferredExecution(&amps_noOpFn, NULL);
3956 }
3957
3958 std::string sow(const MessageHandler& messageHandler_,
3959 const std::string& topic_,
3960 const std::string& filter_ = "",
3961 const std::string& orderBy_ = "",
3962 const std::string& bookmark_ = "",
3963 int batchSize_ = AMPS_DEFAULT_BATCH_SIZE,
3964 int topN_ = AMPS_DEFAULT_TOP_N,
3965 const std::string& options_ = "",
3966 long timeout_ = AMPS_DEFAULT_COMMAND_TIMEOUT)
3967 {
3968 Lock<Mutex> l(_lock);
3969 threadLocalMessage.reset();
3970 threadLocalMessage.setCommandEnum(Message::Command::SOW);
3971 threadLocalMessage.newCommandId();
3972 // need to keep our own copy of the command ID.
3973 std::string commandId = threadLocalMessage.getCommandId();
3974 threadLocalMessage.setQueryID(threadLocalMessage.getCommandId());
3975 unsigned ackTypes = Message::AckType::Processed | Message::AckType::Completed;
3976 threadLocalMessage.setAckTypeEnum(ackTypes);
3977 threadLocalMessage.setTopic(topic_);
3978 if (filter_.length())
3979 {
3980 threadLocalMessage.setFilter(filter_);
3981 }
3982 if (orderBy_.length())
3983 {
3984 threadLocalMessage.setOrderBy(orderBy_);
3985 }
3986 if (bookmark_.length())
3987 {
3988 threadLocalMessage.setBookmark(bookmark_);
3989 }
3990 threadLocalMessage.setBatchSize(AMPS::asString(batchSize_));
3991 if (topN_ != AMPS_DEFAULT_TOP_N)
3992 {
3993 threadLocalMessage.setTopNRecordsReturned(AMPS::asString(topN_));
3994 }
3995 if (options_.length())
3996 {
3997 threadLocalMessage.setOptions(options_);
3998 }
3999
4000 _routes.addRoute(threadLocalMessage.getQueryID(), messageHandler_,
4001 Message::AckType::None, ackTypes, threadLocalMessage.getCommandEnum());
4002
4003 try
4004 {
4005 syncAckProcessing(timeout_, threadLocalMessage);
4006 }
4007 catch (...)
4008 {
4009 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId));
4010 throw;
4011 }
4012
4013 return commandId;
4014 }
4015
4016 std::string sow(const MessageHandler& messageHandler_,
4017 const std::string& topic_,
4018 long timeout_,
4019 const std::string& filter_ = "",
4020 int batchSize_ = AMPS_DEFAULT_BATCH_SIZE,
4021 int topN_ = AMPS_DEFAULT_TOP_N)
4022 {
4023 std::string notSet;
4024 return sow(messageHandler_,
4025 topic_,
4026 filter_,
4027 notSet, // orderBy
4028 notSet, // bookmark
4029 batchSize_,
4030 topN_,
4031 notSet,
4032 timeout_);
4033 }
4034
4035 std::string sowAndSubscribe(const MessageHandler& messageHandler_,
4036 const std::string& topic_,
4037 const std::string& filter_ = "",
4038 const std::string& orderBy_ = "",
4039 const std::string& bookmark_ = "",
4040 int batchSize_ = AMPS_DEFAULT_BATCH_SIZE,
4041 int topN_ = AMPS_DEFAULT_TOP_N,
4042 const std::string& options_ = "",
4043 long timeout_ = AMPS_DEFAULT_COMMAND_TIMEOUT,
4044 bool isHASubscribe_ = true)
4045 {
4046 isHASubscribe_ &= (bool)_subscriptionManager;
4047 unsigned ackTypes = Message::AckType::Processed;
4048 Lock<Mutex> l(_lock);
4049 threadLocalMessage.reset();
4050 threadLocalMessage.setCommandEnum(Message::Command::SOWAndSubscribe);
4051 threadLocalMessage.newCommandId();
4052 Field cid = threadLocalMessage.getCommandId();
4053 std::string subId = cid;
4054 threadLocalMessage.setQueryID(cid).setSubscriptionId(cid).setTopic(topic_);
4055 if (filter_.length())
4056 {
4057 threadLocalMessage.setFilter(filter_);
4058 }
4059 if (orderBy_.length())
4060 {
4061 threadLocalMessage.setOrderBy(orderBy_);
4062 }
4063 if (bookmark_.length())
4064 {
4065 threadLocalMessage.setBookmark(bookmark_);
4066 Message::Field bookmark = threadLocalMessage.getBookmark();
4067 if (_bookmarkStore.isValid())
4068 {
4069 ackTypes |= Message::AckType::Persisted;
4070 if (bookmark == AMPS_BOOKMARK_RECENT)
4071 {
4072 threadLocalMessage.assignOwnershipBookmark(_bookmarkStore.getMostRecent(threadLocalMessage.getSubscriptionId()));
4073 }
4074 else if (bookmark != AMPS_BOOKMARK_NOW &&
4075 bookmark != AMPS_BOOKMARK_EPOCH)
4076 {
4077 _bookmarkStore.log(threadLocalMessage);
4078 if (!BookmarkRange::isRange(bookmark))
4079 {
4080 _bookmarkStore.discard(threadLocalMessage);
4081 _bookmarkStore.persisted(threadLocalMessage.getSubscriptionId(),
4082 bookmark);
4083 }
4084 }
4085 }
4086 else if (bookmark == AMPS_BOOKMARK_RECENT)
4087 {
4088 threadLocalMessage.setBookmark(AMPS_BOOKMARK_EPOCH);
4089 }
4090 }
4091 threadLocalMessage.setBatchSize(AMPS::asString(batchSize_));
4092 if (topN_ != AMPS_DEFAULT_TOP_N)
4093 {
4094 threadLocalMessage.setTopNRecordsReturned(AMPS::asString(topN_));
4095 }
4096 if (options_.length())
4097 {
4098 threadLocalMessage.setOptions(options_);
4099 }
4100
4101 AMPS_ATOMIC_BASE_TYPE generation = (AMPS_ATOMIC_BASE_TYPE)0;
4102 if (isHASubscribe_)
4103 {
4104 {
4105 Unlock<Mutex> u(_lock);
4106 generation =
4107 _subscriptionManager->subscribe(messageHandler_, threadLocalMessage.deepCopy(),
4108 Message::AckType::None);
4109 }
4110 if (generation <= (AMPS_ATOMIC_BASE_TYPE)_subscriptionManagerGenerationCount)
4111 {
4112 return subId;
4113 }
4114 }
4115 _routes.addRoute(cid, messageHandler_,
4116 Message::AckType::None, ackTypes, threadLocalMessage.getCommandEnum());
4117 threadLocalMessage.setAckTypeEnum(ackTypes);
4118 if (!options_.empty())
4119 {
4120 threadLocalMessage.setOptions(options_);
4121 }
4122 try
4123 {
4124 syncAckProcessing(timeout_, threadLocalMessage, 0, generation);
4125 }
4126 catch (const DisconnectedException&)
4127 {
4128 if (!isHASubscribe_)
4129 {
4130 _routes.removeRoute(subId);
4131 throw;
4132 }
4133 }
4134 catch (const TimedOutException&)
4135 {
4136 AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(subId));
4137 throw;
4138 }
4139 catch (...)
4140 {
4141 if (isHASubscribe_)
4142 {
4143 // Have to unlock before calling into sub manager to avoid deadlock
4144 Unlock<Mutex> unlock(_lock);
4145 _subscriptionManager->unsubscribe(cid);
4146 }
4147 _routes.removeRoute(subId);
4148 throw;
4149 }
4150 return subId;
4151 }
4152
4153 std::string sowAndSubscribe(const MessageHandler& messageHandler_,
4154 const std::string& topic_,
4155 long timeout_,
4156 const std::string& filter_ = "",
4157 int batchSize_ = AMPS_DEFAULT_BATCH_SIZE,
4158 bool oofEnabled_ = false,
4159 int topN_ = AMPS_DEFAULT_TOP_N,
4160 bool isHASubscribe_ = true)
4161 {
4162 std::string notSet;
4163 return sowAndSubscribe(messageHandler_,
4164 topic_,
4165 filter_,
4166 notSet, // orderBy
4167 notSet, // bookmark
4168 batchSize_,
4169 topN_,
4170 (oofEnabled_ ? "oof" : ""),
4171 timeout_,
4172 isHASubscribe_);
4173 }
4174
4175 std::string sowAndDeltaSubscribe(const MessageHandler& messageHandler_,
4176 const std::string& topic_,
4177 const std::string& filter_ = "",
4178 const std::string& orderBy_ = "",
4179 int batchSize_ = AMPS_DEFAULT_BATCH_SIZE,
4180 int topN_ = AMPS_DEFAULT_TOP_N,
4181 const std::string& options_ = "",
4182 long timeout_ = AMPS_DEFAULT_COMMAND_TIMEOUT,
4183 bool isHASubscribe_ = true)
4184 {
4185 isHASubscribe_ &= (bool)_subscriptionManager;
4186 Lock<Mutex> l(_lock);
4187 threadLocalMessage.reset();
4188 threadLocalMessage.setCommandEnum(Message::Command::SOWAndDeltaSubscribe);
4189 threadLocalMessage.newCommandId();
4190 threadLocalMessage.setQueryID(threadLocalMessage.getCommandId());
4191 threadLocalMessage.setSubscriptionId(threadLocalMessage.getCommandId());
4192 std::string subId = threadLocalMessage.getSubscriptionId();
4193 threadLocalMessage.setTopic(topic_);
4194 if (filter_.length())
4195 {
4196 threadLocalMessage.setFilter(filter_);
4197 }
4198 if (orderBy_.length())
4199 {
4200 threadLocalMessage.setOrderBy(orderBy_);
4201 }
4202 threadLocalMessage.setBatchSize(AMPS::asString(batchSize_));
4203 if (topN_ != AMPS_DEFAULT_TOP_N)
4204 {
4205 threadLocalMessage.setTopNRecordsReturned(AMPS::asString(topN_));
4206 }
4207 if (options_.length())
4208 {
4209 threadLocalMessage.setOptions(options_);
4210 }
4211 AMPS_ATOMIC_BASE_TYPE generation = (AMPS_ATOMIC_BASE_TYPE)0;
4212 if (isHASubscribe_)
4213 {
4214 {
4215 Unlock<Mutex> u(_lock);
4216 generation =
4217 _subscriptionManager->subscribe(messageHandler_, threadLocalMessage.deepCopy(),
4218 Message::AckType::None);
4219 }
4220 if (generation <= (AMPS_ATOMIC_BASE_TYPE)_subscriptionManagerGenerationCount)
4221 {
4222 return subId;
4223 }
4224 }
4225 _routes.addRoute(threadLocalMessage.getQueryID(), messageHandler_,
4226 Message::AckType::None, Message::AckType::Processed, threadLocalMessage.getCommandEnum());
4227 threadLocalMessage.setAckTypeEnum(Message::AckType::Processed);
4228 if (!options_.empty())
4229 {
4230 threadLocalMessage.setOptions(options_);
4231 }
4232 try
4233 {
4234 syncAckProcessing(timeout_, threadLocalMessage, 0, generation);
4235 }
4236 catch (const DisconnectedException&)
4237 {
4238 if (!isHASubscribe_)
4239 {
4240 _routes.removeRoute(subId);
4241 throw;
4242 }
4243 }
4244 catch (const TimedOutException&)
4245 {
4246 AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(subId));
4247 throw;
4248 }
4249 catch (...)
4250 {
4251 if (isHASubscribe_)
4252 {
4253 // Have to unlock before calling into sub manager to avoid deadlock
4254 Unlock<Mutex> unlock(_lock);
4255 _subscriptionManager->unsubscribe(Field(subId));
4256 }
4257 _routes.removeRoute(subId);
4258 throw;
4259 }
4260 return subId;
4261 }
4262
4263 std::string sowAndDeltaSubscribe(const MessageHandler& messageHandler_,
4264 const std::string& topic_,
4265 long timeout_,
4266 const std::string& filter_ = "",
4267 int batchSize_ = AMPS_DEFAULT_BATCH_SIZE,
4268 bool oofEnabled_ = false,
4269 bool sendEmpties_ = false,
4270 int topN_ = AMPS_DEFAULT_TOP_N,
4271 bool isHASubscribe_ = true)
4272 {
4273 std::string notSet;
4274 Message::Options options;
4275 if (oofEnabled_)
4276 {
4277 options.setOOF();
4278 }
4279 if (sendEmpties_ == false)
4280 {
4281 options.setNoEmpties();
4282 }
4283 return sowAndDeltaSubscribe(messageHandler_,
4284 topic_,
4285 filter_,
4286 notSet, // orderBy
4287 batchSize_,
4288 topN_,
4289 options,
4290 timeout_,
4291 isHASubscribe_);
4292 }
4293
4294 std::string sowDelete(const MessageHandler& messageHandler_,
4295 const std::string& topic_,
4296 const std::string& filter_,
4297 long timeout_,
4298 Message::Field commandId_ = Message::Field())
4299 {
4300 if (_publishStore.isValid())
4301 {
4302 unsigned ackType = Message::AckType::Processed |
4303 Message::AckType::Stats |
4304 Message::AckType::Persisted;
4305 threadLocalMessage.reset();
4306 if (commandId_.empty())
4307 {
4308 threadLocalMessage.newCommandId();
4309 commandId_ = threadLocalMessage.getCommandId();
4310 }
4311 else
4312 {
4313 threadLocalMessage.setCommandId(commandId_.data(), commandId_.len());
4314 }
4315 threadLocalMessage.setCommandEnum(Message::Command::SOWDelete)
4316 .assignSubscriptionId(commandId_.data(), commandId_.len())
4317 .assignQueryID(commandId_.data(), commandId_.len())
4318 .setAckTypeEnum(ackType)
4319 .assignTopic(topic_.c_str(), topic_.length())
4320 .assignFilter(filter_.c_str(), filter_.length());
4321 amps_uint64_t haSequenceNumber = _publishStore.store(threadLocalMessage);
4322 char buf[AMPS_NUMBER_BUFFER_LEN];
4323 size_t pos = convertToCharArray(buf, haSequenceNumber);
4324 threadLocalMessage.assignSequence(buf + pos, AMPS_NUMBER_BUFFER_LEN - pos);
4325 {
4326 try
4327 {
4328 Lock<Mutex> l(_lock);
4329 _routes.addRoute(commandId_, messageHandler_,
4330 Message::AckType::Stats,
4331 Message::AckType::Processed | Message::AckType::Persisted,
4332 threadLocalMessage.getCommandEnum());
4333 syncAckProcessing(timeout_, threadLocalMessage,
4334 haSequenceNumber);
4335 }
4336 catch (const DisconnectedException&)
4337 { // -V565 // -V5002
4338 // Pass - it will get replayed upon reconnect
4339 }
4340 catch (...)
4341 {
4342 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId_));
4343 throw;
4344 }
4345 }
4346 return (std::string)commandId_;
4347 }
4348 else
4349 {
4350 Lock<Mutex> l(_lock);
4351 threadLocalMessage.reset();
4352 if (commandId_.empty())
4353 {
4354 threadLocalMessage.newCommandId();
4355 commandId_ = threadLocalMessage.getCommandId();
4356 }
4357 else
4358 {
4359 threadLocalMessage.setCommandId(commandId_.data(), commandId_.len());
4360 }
4361 threadLocalMessage.setCommandEnum(Message::Command::SOWDelete)
4362 .assignSubscriptionId(commandId_.data(), commandId_.len())
4363 .assignQueryID(commandId_.data(), commandId_.len())
4364 .setAckTypeEnum(Message::AckType::Processed |
4365 Message::AckType::Stats)
4366 .assignTopic(topic_.c_str(), topic_.length())
4367 .assignFilter(filter_.c_str(), filter_.length());
4368 _routes.addRoute(commandId_, messageHandler_,
4369 Message::AckType::Stats,
4370 Message::AckType::Processed,
4371 threadLocalMessage.getCommandEnum());
4372 try
4373 {
4374 syncAckProcessing(timeout_, threadLocalMessage);
4375 }
4376 catch (...)
4377 {
4378 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId_));
4379 throw;
4380 }
4381 return (std::string)commandId_;
4382 }
4383 }
4384
4385 std::string sowDeleteByData(const MessageHandler& messageHandler_,
4386 const std::string& topic_,
4387 const std::string& data_,
4388 long timeout_,
4389 Message::Field commandId_ = Message::Field())
4390 {
4391 if (_publishStore.isValid())
4392 {
4393 unsigned ackType = Message::AckType::Processed |
4394 Message::AckType::Stats |
4395 Message::AckType::Persisted;
4396 threadLocalMessage.reset();
4397 if (commandId_.empty())
4398 {
4399 threadLocalMessage.newCommandId();
4400 commandId_ = threadLocalMessage.getCommandId();
4401 }
4402 else
4403 {
4404 threadLocalMessage.setCommandId(commandId_.data(), commandId_.len());
4405 }
4406 threadLocalMessage.setCommandEnum(Message::Command::SOWDelete)
4407 .assignSubscriptionId(commandId_.data(), commandId_.len())
4408 .assignQueryID(commandId_.data(), commandId_.len())
4409 .setAckTypeEnum(ackType)
4410 .assignTopic(topic_.c_str(), topic_.length())
4411 .assignData(data_.c_str(), data_.length());
4412 amps_uint64_t haSequenceNumber = _publishStore.store(threadLocalMessage);
4413 char buf[AMPS_NUMBER_BUFFER_LEN];
4414 size_t pos = convertToCharArray(buf, haSequenceNumber);
4415 threadLocalMessage.assignSequence(buf + pos, AMPS_NUMBER_BUFFER_LEN - pos);
4416 {
4417 try
4418 {
4419 Lock<Mutex> l(_lock);
4420 _routes.addRoute(commandId_, messageHandler_,
4421 Message::AckType::Stats,
4422 Message::AckType::Processed | Message::AckType::Persisted,
4423 threadLocalMessage.getCommandEnum());
4424 syncAckProcessing(timeout_, threadLocalMessage,
4425 haSequenceNumber);
4426 }
4427 catch (const DisconnectedException&)
4428 { // -V565 // -V5002
4429 // Pass - it will get replayed upon reconnect
4430 }
4431 catch (...)
4432 {
4433 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId_));
4434 throw;
4435 }
4436 }
4437 return (std::string)commandId_;
4438 }
4439 else
4440 {
4441 Lock<Mutex> l(_lock);
4442 threadLocalMessage.reset();
4443 if (commandId_.empty())
4444 {
4445 threadLocalMessage.newCommandId();
4446 commandId_ = threadLocalMessage.getCommandId();
4447 }
4448 else
4449 {
4450 threadLocalMessage.setCommandId(commandId_.data(), commandId_.len());
4451 }
4452 threadLocalMessage.setCommandEnum(Message::Command::SOWDelete)
4453 .assignSubscriptionId(commandId_.data(), commandId_.len())
4454 .assignQueryID(commandId_.data(), commandId_.len())
4455 .setAckTypeEnum(Message::AckType::Processed |
4456 Message::AckType::Stats)
4457 .assignTopic(topic_.c_str(), topic_.length())
4458 .assignData(data_.c_str(), data_.length());
4459 _routes.addRoute(commandId_, messageHandler_,
4460 Message::AckType::Stats,
4461 Message::AckType::Processed,
4462 threadLocalMessage.getCommandEnum());
4463 try
4464 {
4465 syncAckProcessing(timeout_, threadLocalMessage);
4466 }
4467 catch (...)
4468 {
4469 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId_));
4470 throw;
4471 }
4472 return (std::string)commandId_;
4473 }
4474 }
4475
4476 std::string sowDeleteByKeys(const MessageHandler& messageHandler_,
4477 const std::string& topic_,
4478 const std::string& keys_,
4479 long timeout_,
4480 Message::Field commandId_ = Message::Field())
4481 {
4482 if (_publishStore.isValid())
4483 {
4484 unsigned ackType = Message::AckType::Processed |
4485 Message::AckType::Stats |
4486 Message::AckType::Persisted;
4487 threadLocalMessage.reset();
4488 if (commandId_.empty())
4489 {
4490 threadLocalMessage.newCommandId();
4491 commandId_ = threadLocalMessage.getCommandId();
4492 }
4493 else
4494 {
4495 threadLocalMessage.setCommandId(commandId_.data(), commandId_.len());
4496 }
4497 threadLocalMessage.setCommandEnum(Message::Command::SOWDelete)
4498 .assignSubscriptionId(commandId_.data(), commandId_.len())
4499 .assignQueryID(commandId_.data(), commandId_.len())
4500 .setAckTypeEnum(ackType)
4501 .assignTopic(topic_.c_str(), topic_.length())
4502 .assignSowKeys(keys_.c_str(), keys_.length());
4503 amps_uint64_t haSequenceNumber = _publishStore.store(threadLocalMessage);
4504 char buf[AMPS_NUMBER_BUFFER_LEN];
4505 size_t pos = convertToCharArray(buf, haSequenceNumber);
4506 threadLocalMessage.assignSequence(buf + pos, AMPS_NUMBER_BUFFER_LEN - pos);
4507 {
4508 try
4509 {
4510 Lock<Mutex> l(_lock);
4511 _routes.addRoute(commandId_, messageHandler_,
4512 Message::AckType::Stats,
4513 Message::AckType::Processed | Message::AckType::Persisted,
4514 threadLocalMessage.getCommandEnum());
4515 syncAckProcessing(timeout_, threadLocalMessage,
4516 haSequenceNumber);
4517 }
4518 catch (const DisconnectedException&)
4519 { // -V565 // -V5002
4520 // Pass - it will get replayed upon reconnect
4521 }
4522 catch (...)
4523 {
4524 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId_));
4525 throw;
4526 }
4527 }
4528 return (std::string)commandId_;
4529 }
4530 else
4531 {
4532 Lock<Mutex> l(_lock);
4533 threadLocalMessage.reset();
4534 if (commandId_.empty())
4535 {
4536 threadLocalMessage.newCommandId();
4537 commandId_ = threadLocalMessage.getCommandId();
4538 }
4539 else
4540 {
4541 threadLocalMessage.setCommandId(commandId_.data(), commandId_.len());
4542 }
4543 threadLocalMessage.setCommandEnum(Message::Command::SOWDelete)
4544 .assignSubscriptionId(commandId_.data(), commandId_.len())
4545 .assignQueryID(commandId_.data(), commandId_.len())
4546 .setAckTypeEnum(Message::AckType::Processed |
4547 Message::AckType::Stats)
4548 .assignTopic(topic_.c_str(), topic_.length())
4549 .assignSowKeys(keys_.c_str(), keys_.length());
4550 _routes.addRoute(commandId_, messageHandler_,
4551 Message::AckType::Stats,
4552 Message::AckType::Processed,
4553 threadLocalMessage.getCommandEnum());
4554 try
4555 {
4556 syncAckProcessing(timeout_, threadLocalMessage);
4557 }
4558 catch (...)
4559 {
4560 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId_));
4561 throw;
4562 }
4563 return (std::string)commandId_;
4564 }
4565 }
4566
4567 void startTimer(void)
4568 {
4569 if (_serverVersion >= "5.3.2.0")
4570 {
4571 throw CommandException("The start_timer command is deprecated.");
4572 }
4573 Lock<Mutex> l(_lock);
4574 threadLocalMessage.reset();
4575 threadLocalMessage.setCommandEnum(Message::Command::StartTimer);
4576
4577 _send(threadLocalMessage);
4578 }
4579
4580 std::string stopTimer(MessageHandler messageHandler_)
4581 {
4582 if (_serverVersion >= "5.3.2.0")
4583 {
4584 throw CommandException("The stop_timer command is deprecated.");
4585 }
4586 return executeAsync(Command("stop_timer").addAckType("completed"), messageHandler_);
4587 }
4588
4589 amps_handle getHandle(void)
4590 {
4591 return _client;
4592 }
4593
4601 void setExceptionListener(const std::shared_ptr<const ExceptionListener>& pListener_)
4602 {
4603 _pExceptionListener = pListener_;
4604 _exceptionListener = _pExceptionListener.get();
4605 }
4606
4607 void setExceptionListener(const ExceptionListener& listener_)
4608 {
4609 _exceptionListener = &listener_;
4610 }
4611
4612 const ExceptionListener& getExceptionListener(void) const
4613 {
4614 return *_exceptionListener;
4615 }
4616
4617 void setHeartbeat(unsigned heartbeatInterval_, unsigned readTimeout_)
4618 {
4619 if (readTimeout_ < heartbeatInterval_)
4620 {
4621 throw UsageException("The socket read timeout must be >= the heartbeat interval.");
4622 }
4623 Lock<Mutex> l(_lock);
4624 if (_heartbeatInterval != heartbeatInterval_ ||
4625 _readTimeout != readTimeout_)
4626 {
4627 _heartbeatInterval = heartbeatInterval_;
4628 _readTimeout = readTimeout_;
4629 _sendHeartbeat();
4630 }
4631 }
4632
4633 void _sendHeartbeat(void)
4634 {
4635 if (_connected && _heartbeatInterval != 0)
4636 {
4637 std::ostringstream options;
4638 options << "start," << _heartbeatInterval;
4639 _beatMessage.setOptions(options.str());
4640
4641 _heartbeatTimer.setTimeout(_heartbeatInterval * 1000.0);
4642 _heartbeatTimer.start();
4643 try
4644 {
4645 _sendWithoutRetry(_beatMessage);
4646 broadcastConnectionStateChanged(ConnectionStateListener::HeartbeatInitiated);
4647 }
4648 catch (ConnectionException& ex_)
4649 {
4650 // If we are disconnected when we attempt to send, that's OK;
4651 // we'll send this message after we re-connect (if we do).
4652 AMPS_UNHANDLED_EXCEPTION(ex_);
4653 }
4654 _beatMessage.setOptions("beat");
4655 }
4656 amps_result result = AMPS_E_OK;
4657 if (_readTimeout && _connected)
4658 {
4659 result = amps_client_set_read_timeout(_client, (int)_readTimeout);
4660 if (result != AMPS_E_OK && result != AMPS_E_DISCONNECTED)
4661 {
4662 AMPSException::throwFor(_client, result);
4663 }
4664 if (!_queueAckTimeout)
4665 {
4666 result = amps_client_set_idle_time(_client,
4667 (int)(_heartbeatInterval * 1000));
4668 if (result != AMPS_E_OK && result != AMPS_E_DISCONNECTED)
4669 {
4670 AMPSException::throwFor(_client, result);
4671 }
4672 }
4673 }
4674 }
4675
4676 void addConnectionStateListener(ConnectionStateListener* listener_)
4677 {
4678 Lock<Mutex> lock(_lock);
4679 _connectionStateListeners.insert(listener_);
4680 }
4681
4682 void removeConnectionStateListener(ConnectionStateListener* listener_)
4683 {
4684 Lock<Mutex> lock(_lock);
4685 _connectionStateListeners.erase(listener_);
4686 }
4687
4688 void clearConnectionStateListeners()
4689 {
4690 Lock<Mutex> lock(_lock);
4691 _connectionStateListeners.clear();
4692 }
4693
4694 void _registerHandler(Command& command_, Message::Field& cid_,
4695 MessageHandler& handler_, unsigned requestedAcks_,
4696 unsigned systemAddedAcks_, Message::Command::Type commandType_)
4697 {
4698 Message message = command_.getMessage();
4699 Message::Command::Type commandType = message.getCommandEnum();
4700 Message::Field subid = message.getSubscriptionId();
4701 Message::Field qid = message.getQueryID();
4702 // If we have an id, we're good, even if it's an existing route
4703 bool added = qid.len() || subid.len() || cid_.len();
4704 bool cidIsQid = cid_ == qid;
4705 bool cidUnique = !cidIsQid && cid_.len() > 0 && cid_ != subid;
4706 int addedCount = 0;
4707 if (subid.len() > 0)
4708 {
4709 // This can replace a non-subscribe with a matching id
4710 // with a subscription but not another subscription.
4711 addedCount += _routes.addRoute(subid, handler_, requestedAcks_,
4712 systemAddedAcks_, commandType_);
4713 if (!cidUnique
4714 && (commandType == Message::Command::Subscribe
4715 || commandType == Message::Command::DeltaSubscribe))
4716 {
4717 // We don't need to do anything else
4718 cid_ = subid;
4719 return;
4720 }
4721 }
4722 if (qid.len() > 0 && qid != subid
4723 && (commandType == Message::Command::SOW
4724 || commandType == Message::Command::SOWDelete
4725 || commandType == Message::Command::SOWAndSubscribe
4726 || commandType == Message::Command::SOWAndDeltaSubscribe))
4727 {
4728 while (_routes.hasRoute(qid))
4729 {
4730 message.newQueryId();
4731 if (cidIsQid)
4732 {
4733 cid_ = message.getQueryId();
4734 }
4735 qid = message.getQueryId();
4736 }
4737 if (addedCount == 0)
4738 {
4739 _routes.addRoute(qid, handler_, requestedAcks_,
4740 systemAddedAcks_, commandType_);
4741 }
4742 else
4743 {
4744 void* data = NULL;
4745 {
4746 Unlock<Mutex> u(_lock);
4747 data = amps_invoke_copy_route_function(handler_.userData());
4748 }
4749 if (!data)
4750 {
4751 _routes.addRoute(qid, handler_, requestedAcks_,
4752 systemAddedAcks_, commandType_);
4753 }
4754 else
4755 {
4756 _routes.addRoute(qid,
4757 MessageHandler(handler_.function(),
4758 data),
4759 requestedAcks_,
4760 systemAddedAcks_, commandType_);
4761 }
4762 }
4763 ++addedCount;
4764 }
4765 if (cidUnique && requestedAcks_ & ~Message::AckType::Persisted)
4766 {
4767 while (_routes.hasRoute(cid_))
4768 {
4769 cid_ = message.newCommandId().getCommandId();
4770 }
4771 if (addedCount == 0)
4772 {
4773 _routes.addRoute(cid_, handler_, requestedAcks_,
4774 systemAddedAcks_, commandType_);
4775 }
4776 else
4777 {
4778 void* data = NULL;
4779 {
4780 Unlock<Mutex> u(_lock);
4781 data = amps_invoke_copy_route_function(handler_.userData());
4782 }
4783 if (!data)
4784 {
4785 _routes.addRoute(cid_, handler_, requestedAcks_,
4786 systemAddedAcks_, commandType_);
4787 }
4788 else
4789 {
4790 _routes.addRoute(cid_,
4791 MessageHandler(handler_.function(),
4792 data),
4793 requestedAcks_,
4794 systemAddedAcks_, commandType_);
4795 }
4796 }
4797 }
4798 else if ((commandType == Message::Command::Publish ||
4799 commandType == Message::Command::DeltaPublish)
4800 && requestedAcks_ & ~Message::AckType::Persisted)
4801 {
4802 cid_ = command_.getMessage().newCommandId().getCommandId();
4803 _routes.addRoute(cid_, handler_, requestedAcks_,
4804 systemAddedAcks_, commandType_);
4805 added = true;
4806 }
4807 if (!added)
4808 {
4809 throw UsageException("To use a messagehandler, you must also supply a command or subscription ID.");
4810 }
4811 }
4812
4813 std::string executeAsyncNoLock(Command& command_, MessageHandler& handler_,
4814 bool isHASubscribe_ = true)
4815 {
4816 isHASubscribe_ &= (bool)_subscriptionManager;
4817 Message& message = command_.getMessage();
4818 unsigned systemAddedAcks = (handler_.isValid() || command_.hasProcessedAck()) ?
4819 Message::AckType::Processed : Message::AckType::None;
4820 unsigned requestedAcks = message.getAckTypeEnum();
4821 bool isPublishStore = _publishStore.isValid() && command_.needsSequenceNumber();
4822 Message::Command::Type commandType = message.getCommandEnum();
4823 if (commandType == Message::Command::StopTimer)
4824 {
4825 systemAddedAcks |= Message::AckType::Completed;
4826 }
4827 else if (commandType == Message::Command::Unsubscribe)
4828 {
4829 // Clear routes and sub manager
4830 const std::string subId = message.getSubscriptionId();
4831 if (subId == "all")
4832 {
4833 _routes.unsubscribeAll();
4834 if (_subscriptionManager)
4835 {
4836 Unlock<Mutex> unlock(_lock);
4837 _subscriptionManager->clear();
4838 }
4839 }
4840 else
4841 {
4842 _routes.removeRoute(subId);
4843 // Lock is already acquired
4844 if (_subscriptionManager)
4845 {
4846 // Have to unlock before calling into sub manager to avoid deadlock
4847 Unlock<Mutex> unlock(_lock);
4848 _subscriptionManager->unsubscribe(subId);
4849 }
4850 }
4851 // Make sure the clear gets processed by receive thread
4852 deferredExecution(&amps_noOpFn, NULL);
4853 }
4854 Message::Field cid = message.getCommandId();
4855 if (handler_.isValid() && cid.empty())
4856 {
4857 cid = message.newCommandId().getCommandId();
4858 }
4859 if (message.getBookmark().len() > 0)
4860 {
4861 if (command_.isSubscribe())
4862 {
4863 Message::Field bookmark = message.getBookmark();
4864 if (_bookmarkStore.isValid())
4865 {
4866 systemAddedAcks |= Message::AckType::Persisted;
4867 if (bookmark == AMPS_BOOKMARK_RECENT)
4868 {
4869 message.assignOwnershipBookmark(_bookmarkStore.getMostRecent(message.getSubscriptionId()));
4870 }
4871 else if (bookmark != AMPS_BOOKMARK_NOW &&
4872 bookmark != AMPS_BOOKMARK_EPOCH)
4873 {
4874 _bookmarkStore.log(message);
4875 if (!BookmarkRange::isRange(bookmark))
4876 {
4877 _bookmarkStore.discard(message);
4878 _bookmarkStore.persisted(message.getSubscriptionId(),
4879 bookmark);
4880 }
4881 }
4882 }
4883 else if (bookmark == AMPS_BOOKMARK_RECENT)
4884 {
4885 message.setBookmark(AMPS_BOOKMARK_EPOCH);
4886 }
4887 }
4888 }
4889 if (isPublishStore)
4890 {
4891 systemAddedAcks |= Message::AckType::Persisted;
4892 }
4893 bool isSubscribe = command_.isSubscribe();
4894 if (handler_.isValid() && !isSubscribe)
4895 {
4896 _registerHandler(command_, cid, handler_,
4897 requestedAcks, systemAddedAcks, commandType);
4898 }
4899 if (isPublishStore)
4900 {
4901 bool useSyncSend = cid.len() > 0 && command_.hasProcessedAck();
4902 amps_uint64_t haSequenceNumber = (amps_uint64_t)0;
4903 message.setAckTypeEnum(requestedAcks | systemAddedAcks);
4904 {
4905 Unlock<Mutex> u(_lock);
4906 haSequenceNumber = _publishStore.store(message);
4907 }
4908 message.setSequence(haSequenceNumber);
4909 try
4910 {
4911 if (useSyncSend)
4912 {
4913 syncAckProcessing((long)command_.getTimeout(), message,
4914 haSequenceNumber);
4915 }
4916 else
4917 {
4918 _sendSequenced(message, haSequenceNumber,
4919 commandType & (Message::Command::Publish
4920 | Message::Command::DeltaPublish));
4921 }
4922 }
4923 catch (const DisconnectedException&)
4924 {
4925 throw;
4926 }
4927 catch (...)
4928 {
4929 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(cid));
4930 throw;
4931 }
4932 }
4933 else
4934 {
4935 if (isSubscribe)
4936 {
4937 const Message::Field& subId = message.getSubscriptionId();
4938 AMPS_ATOMIC_BASE_TYPE generation = (AMPS_ATOMIC_BASE_TYPE)0;
4939 if (isHASubscribe_)
4940 {
4941 {
4942 Unlock<Mutex> u(_lock);
4943 generation =
4944 _subscriptionManager->subscribe(handler_,
4945 message.deepCopy(),
4946 requestedAcks);
4947 }
4948 if (generation <= (AMPS_ATOMIC_BASE_TYPE)_subscriptionManagerGenerationCount)
4949 {
4950 message.setAckTypeEnum(requestedAcks);
4951 return std::string(subId.data(), subId.len());
4952 }
4953 }
4954 if (handler_.isValid())
4955 {
4956 _registerHandler(command_, cid, handler_,
4957 requestedAcks, systemAddedAcks, commandType);
4958 }
4959 message.setAckTypeEnum(requestedAcks | systemAddedAcks);
4960 try
4961 {
4962 syncAckProcessing((long)command_.getTimeout(), message, 0, generation);
4963 }
4964 catch (const DisconnectedException&)
4965 {
4966 if (!isHASubscribe_)
4967 {
4968 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(cid));
4969 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(subId));
4970 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(message.getQueryId()));
4971 message.setAckTypeEnum(requestedAcks);
4972 throw;
4973 }
4974 }
4975 catch (const TimedOutException&)
4976 {
4977 AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(cid));
4978 AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(subId));
4979 AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(message.getQueryId()));
4980 message.setAckTypeEnum(requestedAcks);
4981 throw;
4982 }
4983 catch (...)
4984 {
4985 if (isHASubscribe_)
4986 {
4987 // Have to unlock before calling into sub manager to avoid deadlock
4988 Unlock<Mutex> unlock(_lock);
4989 _subscriptionManager->unsubscribe(subId);
4990 }
4991 if (message.getQueryID().len() > 0)
4992 {
4993 _routes.removeRoute(message.getQueryID());
4994 }
4995 _routes.removeRoute(cid);
4996 _routes.removeRoute(subId);
4997 message.setAckTypeEnum(requestedAcks);
4998 throw;
4999 }
5000 if (subId.len() > 0)
5001 {
5002 message.setAckTypeEnum(requestedAcks);
5003 return std::string(subId.data(), subId.len());
5004 }
5005 }
5006 else
5007 {
5008 // SOW, Flush, etc. should always be sync. Publish/delete may not be.
5009 bool useSyncSend = commandType & ~Message::Command::NoDataCommands
5010 || (cid.len() > 0 && command_.hasProcessedAck());
5011 message.setAckTypeEnum(requestedAcks | systemAddedAcks);
5012 try
5013 {
5014 if (useSyncSend)
5015 {
5016 syncAckProcessing((long)(command_.getTimeout()), message);
5017 }
5018 else
5019 {
5020 _sendSequenced(message, 0,
5021 commandType & (Message::Command::Publish
5022 | Message::Command::DeltaPublish));
5023 }
5024 }
5025 catch (const TimedOutException&)
5026 {
5027 AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(cid));
5028 AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(message.getQueryId()));
5029 message.setAckTypeEnum(requestedAcks);
5030 throw;
5031 }
5032 catch (const DisconnectedException&)
5033 {
5034 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(cid));
5035 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(message.getQueryId()));
5036 message.setAckTypeEnum(requestedAcks);
5037 throw;
5038 }
5039 catch (...)
5040 {
5041 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(cid));
5042 AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(message.getQueryId()));
5043 message.setAckTypeEnum(requestedAcks);
5044 throw;
5045 }
5046 }
5047 }
5048 message.setAckTypeEnum(requestedAcks);
5049 return cid;
5050 }
5051
5052 MessageStream getEmptyMessageStream(void);
5053
5054 std::string executeAsync(Command& command_, MessageHandler& handler_,
5055 bool isHASubscribe_ = true)
5056 {
5057 Lock<Mutex> lock(_lock);
5058 return executeAsyncNoLock(command_, handler_, isHASubscribe_);
5059 }
5060
5061 // Queue Methods //
5062 void setAutoAck(bool isAutoAckEnabled_)
5063 {
5064 _isAutoAckEnabled = isAutoAckEnabled_;
5065 }
5066 bool getAutoAck(void) const
5067 {
5068 return _isAutoAckEnabled;
5069 }
5070 void setAckBatchSize(const unsigned batchSize_)
5071 {
5072 _ackBatchSize = batchSize_;
5073 if (!_queueAckTimeout)
5074 {
5075 _queueAckTimeout = AMPS_DEFAULT_QUEUE_ACK_TIMEOUT;
5076 amps_client_set_idle_time(_client, _queueAckTimeout);
5077 }
5078 }
5079 unsigned getAckBatchSize(void) const
5080 {
5081 return _ackBatchSize;
5082 }
5083 int getAckTimeout(void) const
5084 {
5085 return _queueAckTimeout;
5086 }
5087 void setAckTimeout(const int ackTimeout_)
5088 {
5089 amps_client_set_idle_time(_client, ackTimeout_);
5090 _queueAckTimeout = ackTimeout_;
5091 }
5092 size_t _ack(QueueBookmarks& queueBookmarks_)
5093 {
5094 if (queueBookmarks_._bookmarkCount)
5095 {
5096 threadLocalMessage.reset();
5097 threadLocalMessage.setCommandEnum(Message::Command::SOWDelete)
5098 .setTopic(queueBookmarks_._topic)
5099 .setBookmark(queueBookmarks_._data)
5100 .setCommandId("AMPS-queue-ack");
5101 amps_uint64_t haSequenceNumber = 0;
5102 if (_publishStore.isValid())
5103 {
5104 haSequenceNumber = _publishStore.store(threadLocalMessage);
5105 threadLocalMessage.setAckType("persisted")
5106 .setSequence(haSequenceNumber);
5107 queueBookmarks_._data.erase();
5108 queueBookmarks_._bookmarkCount = 0;
5109 }
5110 _sendSequenced(threadLocalMessage, haSequenceNumber, 0);
5111 if (!_publishStore.isValid())
5112 {
5113 queueBookmarks_._data.erase();
5114 queueBookmarks_._bookmarkCount = 0;
5115 }
5116 return 1;
5117 }
5118 return 0;
5119 }
5120 void ack(const Field& topic_, const Field& bookmark_, const char* options_ = NULL)
5121 {
5122 if (_isAutoAckEnabled)
5123 {
5124 return;
5125 }
5126 _ack(topic_, bookmark_, options_);
5127 }
5128 void _ack(const Field& topic_, const Field& bookmark_, const char* options_ = NULL)
5129 {
5130 if (bookmark_.len() == 0)
5131 {
5132 return;
5133 }
5134 Lock<Mutex> lock(_lock);
5135 if (_ackBatchSize < 2 || options_ != NULL)
5136 {
5137 threadLocalMessage.reset();
5138 threadLocalMessage.setCommandEnum(Message::Command::SOWDelete)
5139 .setCommandId("AMPS-queue-ack")
5140 .setTopic(topic_).setBookmark(bookmark_);
5141 if (options_)
5142 {
5143 threadLocalMessage.setOptions(options_);
5144 }
5145 amps_uint64_t haSequenceNumber = 0;
5146 if (_publishStore.isValid())
5147 {
5148 haSequenceNumber = _publishStore.store(threadLocalMessage);
5149 threadLocalMessage.setAckType("persisted")
5150 .setSequence(haSequenceNumber);
5151 }
5152 _sendSequenced(threadLocalMessage, haSequenceNumber, 0);
5153 return;
5154 }
5155 // have we acked anything for this hash
5156 topic_hash hash = CRC<0>::crcNoSSE(topic_.data(), topic_.len());
5157 TopicHashMap::iterator it = _topicHashMap.find(hash);
5158 if (it == _topicHashMap.end())
5159 {
5160 // add a new one to the map
5161 it = _topicHashMap.emplace(TopicHashMap::value_type(hash, QueueBookmarks(topic_))).first;
5162 }
5163 QueueBookmarks& queueBookmarks = it->second;
5164 if (queueBookmarks._data.length())
5165 {
5166 queueBookmarks._data.append(",");
5167 }
5168 else
5169 {
5170 queueBookmarks._oldestTime = amps_now();
5171 }
5172 queueBookmarks._data.append(bookmark_);
5173 if (++queueBookmarks._bookmarkCount >= _ackBatchSize)
5174 {
5175 _ack(queueBookmarks);
5176 }
5177 }
5178 void flushAcks(void)
5179 {
5180 size_t sendCount = 0;
5181 if (!_connected)
5182 {
5183 return;
5184 }
5185 else
5186 {
5187 Lock<Mutex> lock(_lock);
5188 typedef TopicHashMap::iterator iterator;
5189 for (iterator it = _topicHashMap.begin(), end = _topicHashMap.end(); it != end; ++it)
5190 {
5191 QueueBookmarks& queueBookmarks = it->second;
5192 sendCount += _ack(queueBookmarks);
5193 }
5194 }
5195 if (sendCount && _connected)
5196 {
5197 publishFlush(0, Message::AckType::Processed);
5198 }
5199 }
5200 // called when there's idle time, to see if we need to flush out any "acks"
5201 void checkQueueAcks(void)
5202 {
5203 if (!_topicHashMap.size())
5204 {
5205 return;
5206 }
5207 Lock<Mutex> lock(_lock);
5208 try
5209 {
5210 amps_uint64_t threshold = amps_now()
5211 - (amps_uint64_t)_queueAckTimeout;
5212 typedef TopicHashMap::iterator iterator;
5213 for (iterator it = _topicHashMap.begin(), end = _topicHashMap.end(); it != end; ++it)
5214 {
5215 QueueBookmarks& queueBookmarks = it->second;
5216 if (queueBookmarks._bookmarkCount && queueBookmarks._oldestTime < threshold)
5217 {
5218 _ack(queueBookmarks);
5219 }
5220 }
5221 }
5222 catch (std::exception& ex)
5223 {
5224 AMPS_UNHANDLED_EXCEPTION(ex);
5225 }
5226 }
5227
5228 void deferredExecution(DeferredExecutionFunc func_, void* userData_)
5229 {
5230 Lock<Mutex> lock(_deferredExecutionLock);
5231 _deferredExecutionList.emplace_back(
5232 DeferredExecutionRequest(func_, userData_));
5233 }
5234
5235 inline void processDeferredExecutions(void)
5236 {
5237 if (_deferredExecutionList.size())
5238 {
5239 Lock<Mutex> lock(_deferredExecutionLock);
5240 DeferredExecutionList::iterator it = _deferredExecutionList.begin();
5241 DeferredExecutionList::iterator end = _deferredExecutionList.end();
5242 for (; it != end; ++it)
5243 {
5244 try
5245 {
5246 it->_func(it->_userData);
5247 }
5248 catch (...)
5249 { // -V565 // -V5002
5250 // Intentionally ignore errors
5251 }
5252 }
5253 _deferredExecutionList.clear();
5254 _routes.invalidateCache();
5255 _routeCache.invalidateCache();
5256 }
5257 }
5258
5259 bool getRetryOnDisconnect(void) const
5260 {
5261 return _isRetryOnDisconnect;
5262 }
5263
5264 void setRetryOnDisconnect(bool isRetryOnDisconnect_)
5265 {
5266 _isRetryOnDisconnect = isRetryOnDisconnect_;
5267 }
5268
5269 void setDefaultMaxDepth(unsigned maxDepth_)
5270 {
5271 _defaultMaxDepth = maxDepth_;
5272 }
5273
5274 unsigned getDefaultMaxDepth(void) const
5275 {
5276 return _defaultMaxDepth;
5277 }
5278
5279 void setTransportFilterFunction(amps_transport_filter_function filter_,
5280 void* userData_)
5281 {
5282 amps_client_set_transport_filter_function(_client, filter_, userData_);
5283 }
5284
5285 void setThreadCreatedCallback(amps_thread_created_callback callback_,
5286 void* userData_)
5287 {
5288 amps_client_set_thread_created_callback(_client, callback_, userData_);
5289 }
5290
5291 void setPublishBatching(amps_uint64_t batchSizeBytes_, amps_uint64_t batchTimeoutMillis_)
5292 {
5293 amps_client_set_batch_send(_client, batchSizeBytes_, batchTimeoutMillis_);
5294 }
5295 }; // class ClientImpl
5296
5371
5372 class MessageStream
5373 {
5374 RefHandle<MessageStreamImpl> _body;
5375 public:
5380 class iterator
5381 {
5382 MessageStream* _pStream;
5383 Message _current;
5384 inline void advance(void);
5385
5386 public:
5387 iterator() // end
5388 : _pStream(NULL)
5389 {;}
5390 iterator(MessageStream* pStream_)
5391 : _pStream(pStream_)
5392 {
5393 advance();
5394 }
5395
5396 bool operator==(const iterator& rhs) const
5397 {
5398 return _pStream == rhs._pStream;
5399 }
5400 bool operator!=(const iterator& rhs) const
5401 {
5402 return _pStream != rhs._pStream;
5403 }
5404 void operator++(void)
5405 {
5406 advance();
5407 }
5408 Message operator*(void)
5409 {
5410 return _current;
5411 }
5412 Message* operator->(void)
5413 {
5414 return &_current;
5415 }
5416 };
5418 bool isValid() const
5419 {
5420 return _body.isValid();
5421 }
5422
5425 iterator begin(void)
5426 {
5427 if (!_body.isValid())
5428 {
5429 throw UsageException("This MessageStream is not valid and cannot be iterated.");
5430 }
5431 return iterator(this);
5432 }
5435 // For non-SOW queries, the end is never reached.
5436 iterator end(void)
5437 {
5438 return iterator();
5439 }
5440 inline MessageStream(void) = default;
5441
5447 MessageStream timeout(unsigned timeout_);
5448
5452 MessageStream conflate(void);
5458 MessageStream maxDepth(unsigned maxDepth_);
5461 unsigned getMaxDepth(void) const;
5464 unsigned getDepth(void) const;
5465
5466 private:
5467 inline MessageStream(const Client& client_);
5468 inline MessageStream(RefHandle<MessageStreamImpl> body_);
5469 inline void setSOWOnly(const std::string& commandId_,
5470 const std::string& queryId_ = "");
5471 inline void setSubscription(const std::string& subId_,
5472 const std::string& commandId_ = "",
5473 const std::string& queryId_ = "");
5474 inline void setStatsOnly(const std::string& commandId_,
5475 const std::string& queryId_ = "");
5476 inline void setAcksOnly(const std::string& commandId_, unsigned acks_);
5477
5478 inline operator MessageHandler(void);
5479
5480 inline static MessageStream fromExistingHandler(const MessageHandler& handler);
5481
5482 friend class Client;
5483 friend class ClientImpl;
5484
5485 };
5486
5506 class Client // -V553
5507 {
5508 protected:
5509 BorrowRefHandle<ClientImpl> _body;
5510 public:
5511 static const int DEFAULT_COMMAND_TIMEOUT = AMPS_DEFAULT_COMMAND_TIMEOUT;
5512 static const int DEFAULT_BATCH_SIZE = AMPS_DEFAULT_BATCH_SIZE;
5513 static const int DEFAULT_TOP_N = AMPS_DEFAULT_TOP_N;
5514
5523 Client(const std::string& clientName = "")
5524 : _body(new ClientImpl(clientName), true)
5525 {;}
5526
5527 Client(ClientImpl* existingClient)
5528 : _body(existingClient, true)
5529 {;}
5530
5531 Client(ClientImpl* existingClient, bool isRef)
5532 : _body(existingClient, isRef)
5533 {;}
5534
5535 Client(const Client& rhs) : _body(rhs._body) {;}
5536 virtual ~Client(void) {;}
5537
5538 Client& operator=(const Client& rhs)
5539 {
5540 _body = rhs._body;
5541 return *this;
5542 }
5543
5544 bool isValid()
5545 {
5546 return _body.isValid();
5547 }
5548
5561 void setName(const std::string& name)
5562 {
5563 _body.get().setName(name);
5564 }
5565
5568 const std::string& getName() const
5569 {
5570 return _body.get().getName();
5571 }
5572
5576 const std::string& getNameHash() const
5577 {
5578 return _body.get().getNameHash();
5579 }
5580
5584 const amps_uint64_t getNameHashValue() const
5585 {
5586 return _body.get().getNameHashValue();
5587 }
5588
5592 const std::string& getConnectionName() const
5593 {
5594 return _body.get().getConnectionName();
5595 }
5596
5603 void setLogonCorrelationData(const std::string& logonCorrelationData_)
5604 {
5605 _body.get().setLogonCorrelationData(logonCorrelationData_);
5606 }
5607
5610 const std::string& getLogonCorrelationData() const
5611 {
5612 return _body.get().getLogonCorrelationData();
5613 }
5614
5618 void addHttpPreflightHeader(const std::string& header_)
5619 {
5620 _body.get().addHttpPreflightHeader(header_);
5621 }
5622
5627 void addHttpPreflightHeader(const std::string& key_, const std::string& value_)
5628 {
5629 _body.get().addHttpPreflightHeader(key_, value_);
5630 }
5631
5633 void clearHttpPreflightHeaders()
5634 {
5635 _body.get().clearHttpPreflightHeaders();
5636 }
5637
5641 template<class T>
5642 void setHttpPreflightHeaders(const T& headers_)
5643 {
5644 _body.get().setHttpPreflightHeaders(headers_);
5645 }
5646
5655 size_t getServerVersion() const
5656 {
5657 return _body.get().getServerVersion();
5658 }
5659
5666 VersionInfo getServerVersionInfo() const
5667 {
5668 return _body.get().getServerVersionInfo();
5669 }
5670
5680 static size_t convertVersionToNumber(const std::string& version_)
5681 {
5682 return AMPS::convertVersionToNumber(version_.c_str(), version_.length());
5683 }
5684
5695 static size_t convertVersionToNumber(const char* data_, size_t len_)
5696 {
5697 return AMPS::convertVersionToNumber(data_, len_);
5698 }
5699
5702 const std::string& getURI() const
5703 {
5704 return _body.get().getURI();
5705 }
5706
5713
5715
5726 void connect(const std::string& uri)
5727 {
5728 _body.get().connect(uri);
5729 }
5730
5733 void disconnect()
5734 {
5735 _body.get().disconnect();
5736 }
5737
5751 void send(const Message& message)
5752 {
5753 _body.get().send(message);
5754 }
5755
5764 void addMessageHandler(const Field& commandId_,
5765 const AMPS::MessageHandler& messageHandler_,
5766 unsigned requestedAcks_, bool isSubscribe_)
5767 {
5768 Message::Command::Type commandType = isSubscribe_ ? Message::Command::Subscribe : Message::Command::SOW;
5769 _body.get().addMessageHandler(commandId_, messageHandler_,
5770 requestedAcks_, commandType);
5771 }
5772
5781 void addMessageHandler(const Field& commandId_,
5782 const AMPS::MessageHandler& messageHandler_,
5783 unsigned requestedAcks_, Message::Command::Type commandType_)
5784 {
5785 _body.get().addMessageHandler(commandId_, messageHandler_,
5786 requestedAcks_, commandType_);
5787 }
5788
5792 bool removeMessageHandler(const Field& commandId_)
5793 {
5794 return _body.get().removeMessageHandler(commandId_);
5795 }
5796
5821 std::string send(const MessageHandler& messageHandler_,
5822 Message& message_,
5823 int timeout_ = 0,
5824 AMPS_ATOMIC_BASE_TYPE subscribeGeneration_ = 0)
5825 {
5826 return _body.get().send(messageHandler_, message_, timeout_, subscribeGeneration_);
5827 }
5828
5842 #if defined(_WIN32) || __cplusplus >= 201402L
5843 [[deprecated("Use HAClient for automatic reconnection and a ConnectionStateListener to monitor connection state.")]]
5844 #endif
5845 virtual void setDisconnectHandler(const DisconnectHandler& disconnectHandler)
5846 {
5847 _body.get().setDisconnectHandler(disconnectHandler);
5848 }
5849
5856 #if defined(_WIN32) || __cplusplus >= 201402L
5857 [[deprecated("Use HAClient for automatic reconnection and a ConnectionStateListener to monitor connection state.")]]
5858 #endif
5859 DisconnectHandler getDisconnectHandler(void) const
5860 {
5861 return _body.get().getDisconnectHandler();
5862 }
5863
5868 virtual ConnectionInfo getConnectionInfo() const
5869 {
5870 return _body.get().getConnectionInfo();
5871 }
5872
5881 void setBookmarkStore(const BookmarkStore& bookmarkStore_)
5882 {
5883 _body.get().setBookmarkStore(bookmarkStore_);
5884 }
5885
5889 BookmarkStore getBookmarkStore()
5890 {
5891 return _body.get().getBookmarkStore();
5892 }
5893
5897 SubscriptionManager* getSubscriptionManager()
5898 {
5899 return _body.get().getSubscriptionManager();
5900 }
5901
5909 void setSubscriptionManager(SubscriptionManager* subscriptionManager_)
5910 {
5911 _body.get().setSubscriptionManager(subscriptionManager_);
5912 }
5913
5933 void setPublishStore(const Store& publishStore_)
5934 {
5935 _body.get().setPublishStore(publishStore_);
5936 }
5937
5941 Store getPublishStore()
5942 {
5943 return _body.get().getPublishStore();
5944 }
5945
5949 void setDuplicateMessageHandler(const MessageHandler& duplicateMessageHandler_)
5950 {
5951 _body.get().setGlobalCommandTypeMessageHandler(ClientImpl::GlobalCommandTypeHandlers::DuplicateMessage,
5952 duplicateMessageHandler_);
5953 }
5954
5964 MessageHandler getDuplicateMessageHandler(void)
5965 {
5966 return _body.get().getDuplicateMessageHandler();
5967 }
5968
5978 void setFailedWriteHandler(FailedWriteHandler* handler_)
5979 {
5980 _body.get().setFailedWriteHandler(handler_);
5981 }
5982
5986 FailedWriteHandler* getFailedWriteHandler()
5987 {
5988 return _body.get().getFailedWriteHandler();
5989 }
5990
5991
6009 amps_uint64_t publish(const std::string& topic_, const std::string& data_)
6010 {
6011 return _body.get().publish(topic_.c_str(), topic_.length(),
6012 data_.c_str(), data_.length());
6013 }
6014
6034 amps_uint64_t publish(const char* topic_, size_t topicLength_,
6035 const char* data_, size_t dataLength_)
6036 {
6037 return _body.get().publish(topic_, topicLength_, data_, dataLength_);
6038 }
6039
6058 amps_uint64_t publish(const std::string& topic_, const std::string& data_,
6059 unsigned long expiration_)
6060 {
6061 return _body.get().publish(topic_.c_str(), topic_.length(),
6062 data_.c_str(), data_.length(), expiration_);
6063 }
6064
6085 amps_uint64_t publish(const char* topic_, size_t topicLength_,
6086 const char* data_, size_t dataLength_,
6087 unsigned long expiration_)
6088 {
6089 return _body.get().publish(topic_, topicLength_,
6090 data_, dataLength_, expiration_);
6091 }
6092
6111 amps_uint64_t publish(const std::string& topic_, const std::string& data_,
6112 const std::string& correlationId_)
6113 {
6114 return _body.get().publish(topic_.c_str(), topic_.length(),
6115 data_.c_str(), data_.length(),
6116 correlationId_.c_str(), correlationId_.length());
6117 }
6118
6140 amps_uint64_t publish(const char* topic_, size_t topicLength_,
6141 const char* data_, size_t dataLength_,
6142 const char* correlationId_, size_t correlationIdLength_)
6143 {
6144 return _body.get().publish(topic_, topicLength_, data_, dataLength_,
6145 correlationId_, correlationIdLength_);
6146 }
6147
6167 amps_uint64_t publish(const std::string& topic_, const std::string& data_,
6168 const std::string& correlationId_,
6169 unsigned long expiration_)
6170 {
6171 return _body.get().publish(topic_.c_str(), topic_.length(),
6172 data_.c_str(), data_.length(),
6173 correlationId_.c_str(), correlationId_.length(),
6174 expiration_);
6175 }
6176
6199 amps_uint64_t publish(const char* topic_, size_t topicLength_,
6200 const char* data_, size_t dataLength_,
6201 const char* correlationId_, size_t correlationIdLength_,
6202 unsigned long expiration_)
6203 {
6204 return _body.get().publish(topic_, topicLength_, data_, dataLength_,
6205 correlationId_, correlationIdLength_,
6206 expiration_);
6207 }
6208
6228 amps_uint64_t publish(const std::string& topic_, const std::string& data_,
6229 const std::string& correlationId_,
6230 const std::string& sowKey_)
6231 {
6232 return _body.get().publish(topic_.c_str(), topic_.length(),
6233 data_.c_str(), data_.length(),
6234 correlationId_.c_str(), correlationId_.length(),
6235 sowKey_.c_str(), sowKey_.length());
6236 }
6237
6261 amps_uint64_t publish(const char* topic_, size_t topicLength_,
6262 const char* data_, size_t dataLength_,
6263 const char* correlationId_, size_t correlationIdLength_,
6264 const char* sowKey_, size_t sowKeyLength_)
6265 {
6266 return _body.get().publish(topic_, topicLength_, data_, dataLength_,
6267 correlationId_, correlationIdLength_,
6268 sowKey_, sowKeyLength_);
6269 }
6270
6291 amps_uint64_t publish(const std::string& topic_, const std::string& data_,
6292 const std::string& correlationId_,
6293 const std::string& sowKey_,
6294 unsigned long expiration_)
6295 {
6296 return _body.get().publish(topic_.c_str(), topic_.length(),
6297 data_.c_str(), data_.length(),
6298 correlationId_.c_str(), correlationId_.length(),
6299 sowKey_.c_str(), sowKey_.length(),
6300 expiration_);
6301 }
6302
6327 amps_uint64_t publish(const char* topic_, size_t topicLength_,
6328 const char* data_, size_t dataLength_,
6329 const char* correlationId_, size_t correlationIdLength_,
6330 const char* sowKey_, size_t sowKeyLength_,
6331 unsigned long expiration_)
6332 {
6333 return _body.get().publish(topic_, topicLength_,
6334 data_, dataLength_,
6335 correlationId_, correlationIdLength_,
6336 sowKey_, sowKeyLength_,
6337 expiration_);
6338 }
6339
6356 amps_uint64_t publish(Message& message_)
6357 {
6358 return _body.get().publish(message_);
6359 }
6360
6379 amps_uint64_t publishKeyed(const std::string& topic_, const std::string& data_,
6380 const std::string& sowKey_)
6381 {
6382 return _body.get().publishKeyed(topic_.c_str(), topic_.length(),
6383 data_.c_str(), data_.length(),
6384 sowKey_.c_str(), sowKey_.length());
6385 }
6386
6408 amps_uint64_t publishKeyed(const char* topic_, size_t topicLength_,
6409 const char* data_, size_t dataLength_,
6410 const char* sowKey_, size_t sowKeyLength_)
6411 {
6412 return _body.get().publishKeyed(topic_, topicLength_, data_, dataLength_,
6413 sowKey_, sowKeyLength_);
6414 }
6415
6435 amps_uint64_t publishKeyed(const std::string& topic_, const std::string& data_,
6436 const std::string& sowKey_,
6437 unsigned long expiration_)
6438 {
6439 return _body.get().publishKeyed(topic_.c_str(), topic_.length(),
6440 data_.c_str(), data_.length(),
6441 sowKey_.c_str(), sowKey_.length(),
6442 expiration_);
6443 }
6444
6467 amps_uint64_t publishKeyed(const char* topic_, size_t topicLength_,
6468 const char* data_, size_t dataLength_,
6469 const char* sowKey_, size_t sowKeyLength_,
6470 unsigned long expiration_)
6471 {
6472 return _body.get().publishKeyed(topic_, topicLength_,
6473 data_, dataLength_,
6474 sowKey_, sowKeyLength_,
6475 expiration_);
6476 }
6477
6516 void publishFlush(long timeout_ = 0, unsigned ackType_ = Message::AckType::Processed)
6517 {
6518 _body.get().publishFlush(timeout_, ackType_);
6519 }
6520
6521
6537 amps_uint64_t deltaPublish(const std::string& topic_, const std::string& data_)
6538 {
6539 return _body.get().deltaPublish(topic_.c_str(), topic_.length(),
6540 data_.c_str(), data_.length());
6541 }
6542
6560 amps_uint64_t deltaPublish(const char* topic_, size_t topicLength_,
6561 const char* data_, size_t dataLength_)
6562 {
6563 return _body.get().deltaPublish(topic_, topicLength_,
6564 data_, dataLength_);
6565 }
6566
6583 amps_uint64_t deltaPublish(const std::string& topic_, const std::string& data_,
6584 unsigned long expiration_)
6585 {
6586 return _body.get().deltaPublish(topic_.c_str(), topic_.length(),
6587 data_.c_str(), data_.length(),
6588 expiration_);
6589 }
6590
6609 amps_uint64_t deltaPublish(const char* topic_, size_t topicLength_,
6610 const char* data_, size_t dataLength_,
6611 unsigned long expiration_)
6612 {
6613 return _body.get().deltaPublish(topic_, topicLength_,
6614 data_, dataLength_, expiration_);
6615 }
6616
6632 std::string logon(int timeout_ = 0,
6633 Authenticator& authenticator_ = DefaultAuthenticator::instance(),
6634 const char* options_ = NULL)
6635 {
6636 return _body.get().logon(timeout_, authenticator_, options_);
6637 }
6651 std::string logon(const char* options_, int timeout_ = 0)
6652 {
6653 return _body.get().logon(timeout_, DefaultAuthenticator::instance(),
6654 options_);
6655 }
6656
6670 std::string logon(const std::string& options_, int timeout_ = 0)
6671 {
6672 return _body.get().logon(timeout_, DefaultAuthenticator::instance(),
6673 options_.c_str());
6674 }
6675
6695 std::string subscribe(const MessageHandler& messageHandler_,
6696 const std::string& topic_,
6697 long timeout_ = 0,
6698 const std::string& filter_ = "",
6699 const std::string& options_ = "",
6700 const std::string& subId_ = "")
6701 {
6702 return _body.get().subscribe(messageHandler_, topic_, timeout_,
6703 filter_, "", options_, subId_);
6704 }
6705
6721 MessageStream subscribe(const std::string& topic_,
6722 long timeout_ = 0, const std::string& filter_ = "",
6723 const std::string& options_ = "",
6724 const std::string& subId_ = "")
6725 {
6726 MessageStream result(*this);
6727 if (_body.get().getDefaultMaxDepth())
6728 {
6729 result.maxDepth(_body.get().getDefaultMaxDepth());
6730 }
6731 result.setSubscription(_body.get().subscribe(
6732 result.operator MessageHandler(),
6733 topic_, timeout_, filter_, "",
6734 options_, subId_, false));
6735 return result;
6736 }
6737
6753 MessageStream subscribe(const char* topic_,
6754 long timeout_ = 0, const std::string& filter_ = "",
6755 const std::string& options_ = "",
6756 const std::string& subId_ = "")
6757 {
6758 MessageStream result(*this);
6759 if (_body.get().getDefaultMaxDepth())
6760 {
6761 result.maxDepth(_body.get().getDefaultMaxDepth());
6762 }
6763 result.setSubscription(_body.get().subscribe(
6764 result.operator MessageHandler(),
6765 topic_, timeout_, filter_, "",
6766 options_, subId_, false));
6767 return result;
6768 }
6769
6782 std::string deltaSubscribe(const MessageHandler& messageHandler_,
6783 const std::string& topic_,
6784 long timeout_,
6785 const std::string& filter_ = "",
6786 const std::string& options_ = "",
6787 const std::string& subId_ = "")
6788 {
6789 return _body.get().deltaSubscribe(messageHandler_, topic_, timeout_,
6790 filter_, "", options_, subId_);
6791 }
6800 MessageStream deltaSubscribe(const std::string& topic_,
6801 long timeout_, const std::string& filter_ = "",
6802 const std::string& options_ = "",
6803 const std::string& subId_ = "")
6804 {
6805 MessageStream result(*this);
6806 if (_body.get().getDefaultMaxDepth())
6807 {
6808 result.maxDepth(_body.get().getDefaultMaxDepth());
6809 }
6810 result.setSubscription(_body.get().deltaSubscribe(
6811 result.operator MessageHandler(),
6812 topic_, timeout_, filter_, "",
6813 options_, subId_, false));
6814 return result;
6815 }
6816
6818 MessageStream deltaSubscribe(const char* topic_,
6819 long timeout_, const std::string& filter_ = "",
6820 const std::string& options_ = "",
6821 const std::string& subId_ = "")
6822 {
6823 MessageStream result(*this);
6824 if (_body.get().getDefaultMaxDepth())
6825 {
6826 result.maxDepth(_body.get().getDefaultMaxDepth());
6827 }
6828 result.setSubscription(_body.get().deltaSubscribe(
6829 result.operator MessageHandler(),
6830 topic_, timeout_, filter_, "",
6831 options_, subId_, false));
6832 return result;
6833 }
6834
6860 std::string bookmarkSubscribe(const MessageHandler& messageHandler_,
6861 const std::string& topic_,
6862 long timeout_,
6863 const std::string& bookmark_,
6864 const std::string& filter_ = "",
6865 const std::string& options_ = "",
6866 const std::string& subId_ = "")
6867 {
6868 return _body.get().subscribe(messageHandler_, topic_, timeout_,
6869 filter_, bookmark_, options_, subId_);
6870 }
6888 MessageStream bookmarkSubscribe(const std::string& topic_,
6889 long timeout_,
6890 const std::string& bookmark_,
6891 const std::string& filter_ = "",
6892 const std::string& options_ = "",
6893 const std::string& subId_ = "")
6894 {
6895 MessageStream result(*this);
6896 if (_body.get().getDefaultMaxDepth())
6897 {
6898 result.maxDepth(_body.get().getDefaultMaxDepth());
6899 }
6900 result.setSubscription(_body.get().subscribe(
6901 result.operator MessageHandler(),
6902 topic_, timeout_, filter_,
6903 bookmark_, options_,
6904 subId_, false));
6905 return result;
6906 }
6907
6909 MessageStream bookmarkSubscribe(const char* topic_,
6910 long timeout_,
6911 const std::string& bookmark_,
6912 const std::string& filter_ = "",
6913 const std::string& options_ = "",
6914 const std::string& subId_ = "")
6915 {
6916 MessageStream result(*this);
6917 if (_body.get().getDefaultMaxDepth())
6918 {
6919 result.maxDepth(_body.get().getDefaultMaxDepth());
6920 }
6921 result.setSubscription(_body.get().subscribe(
6922 result.operator MessageHandler(),
6923 topic_, timeout_, filter_,
6924 bookmark_, options_,
6925 subId_, false));
6926 return result;
6927 }
6928
6937 void unsubscribe(const std::string& commandId)
6938 {
6939 return _body.get().unsubscribe(commandId);
6940 }
6941
6949 void unsubscribe()
6950 {
6951 return _body.get().unsubscribe();
6952 }
6953
6954
6984 std::string sow(const MessageHandler& messageHandler_,
6985 const std::string& topic_,
6986 const std::string& filter_ = "",
6987 const std::string& orderBy_ = "",
6988 const std::string& bookmark_ = "",
6989 int batchSize_ = DEFAULT_BATCH_SIZE,
6990 int topN_ = DEFAULT_TOP_N,
6991 const std::string& options_ = "",
6992 long timeout_ = DEFAULT_COMMAND_TIMEOUT)
6993 {
6994 return _body.get().sow(messageHandler_, topic_, filter_, orderBy_,
6995 bookmark_, batchSize_, topN_, options_,
6996 timeout_);
6997 }
7022 MessageStream sow(const std::string& topic_,
7023 const std::string& filter_ = "",
7024 const std::string& orderBy_ = "",
7025 const std::string& bookmark_ = "",
7026 int batchSize_ = DEFAULT_BATCH_SIZE,
7027 int topN_ = DEFAULT_TOP_N,
7028 const std::string& options_ = "",
7029 long timeout_ = DEFAULT_COMMAND_TIMEOUT)
7030 {
7031 MessageStream result(*this);
7032 if (_body.get().getDefaultMaxDepth())
7033 {
7034 result.maxDepth(_body.get().getDefaultMaxDepth());
7035 }
7036 result.setSOWOnly(_body.get().sow(result.operator MessageHandler(),
7037 topic_, filter_, orderBy_, bookmark_,
7038 batchSize_, topN_, options_, timeout_));
7039 return result;
7040 }
7041
7043 MessageStream sow(const char* topic_,
7044 const std::string& filter_ = "",
7045 const std::string& orderBy_ = "",
7046 const std::string& bookmark_ = "",
7047 int batchSize_ = DEFAULT_BATCH_SIZE,
7048 int topN_ = DEFAULT_TOP_N,
7049 const std::string& options_ = "",
7050 long timeout_ = DEFAULT_COMMAND_TIMEOUT)
7051 {
7052 MessageStream result(*this);
7053 if (_body.get().getDefaultMaxDepth())
7054 {
7055 result.maxDepth(_body.get().getDefaultMaxDepth());
7056 }
7057 result.setSOWOnly(_body.get().sow(result.operator MessageHandler(),
7058 topic_, filter_, orderBy_, bookmark_,
7059 batchSize_, topN_, options_, timeout_));
7060 return result;
7061 }
7084 std::string sow(const MessageHandler& messageHandler_,
7085 const std::string& topic_,
7086 long timeout_,
7087 const std::string& filter_ = "",
7088 int batchSize_ = DEFAULT_BATCH_SIZE,
7089 int topN_ = DEFAULT_TOP_N)
7090 {
7091 return _body.get().sow(messageHandler_, topic_, timeout_, filter_,
7092 batchSize_, topN_);
7093 }
7116 std::string sowAndSubscribe(const MessageHandler& messageHandler_,
7117 const std::string& topic_,
7118 long timeout_,
7119 const std::string& filter_ = "",
7120 int batchSize_ = DEFAULT_BATCH_SIZE,
7121 bool oofEnabled_ = false,
7122 int topN_ = DEFAULT_TOP_N)
7123 {
7124 return _body.get().sowAndSubscribe(messageHandler_, topic_, timeout_,
7125 filter_, batchSize_, oofEnabled_,
7126 topN_);
7127 }
7128
7148 MessageStream sowAndSubscribe(const std::string& topic_,
7149 long timeout_,
7150 const std::string& filter_ = "",
7151 int batchSize_ = DEFAULT_BATCH_SIZE,
7152 bool oofEnabled_ = false,
7153 int topN_ = DEFAULT_TOP_N)
7154 {
7155 MessageStream result(*this);
7156 if (_body.get().getDefaultMaxDepth())
7157 {
7158 result.maxDepth(_body.get().getDefaultMaxDepth());
7159 }
7160 result.setSubscription(_body.get().sowAndSubscribe(
7161 result.operator MessageHandler(),
7162 topic_, timeout_, filter_,
7163 batchSize_, oofEnabled_,
7164 topN_, false));
7165 return result;
7166 }
7186 MessageStream sowAndSubscribe(const char* topic_,
7187 long timeout_,
7188 const std::string& filter_ = "",
7189 int batchSize_ = DEFAULT_BATCH_SIZE,
7190 bool oofEnabled_ = false,
7191 int topN_ = DEFAULT_TOP_N)
7192 {
7193 MessageStream result(*this);
7194 if (_body.get().getDefaultMaxDepth())
7195 {
7196 result.maxDepth(_body.get().getDefaultMaxDepth());
7197 }
7198 result.setSubscription(_body.get().sowAndSubscribe(
7199 result.operator MessageHandler(),
7200 topic_, timeout_, filter_,
7201 batchSize_, oofEnabled_,
7202 topN_, false));
7203 return result;
7204 }
7205
7206
7234 std::string sowAndSubscribe(const MessageHandler& messageHandler_,
7235 const std::string& topic_,
7236 const std::string& filter_ = "",
7237 const std::string& orderBy_ = "",
7238 const std::string& bookmark_ = "",
7239 int batchSize_ = DEFAULT_BATCH_SIZE,
7240 int topN_ = DEFAULT_TOP_N,
7241 const std::string& options_ = "",
7242 long timeout_ = DEFAULT_COMMAND_TIMEOUT)
7243 {
7244 return _body.get().sowAndSubscribe(messageHandler_, topic_, filter_,
7245 orderBy_, bookmark_, batchSize_,
7246 topN_, options_, timeout_);
7247 }
7248
7273 MessageStream sowAndSubscribe(const std::string& topic_,
7274 const std::string& filter_ = "",
7275 const std::string& orderBy_ = "",
7276 const std::string& bookmark_ = "",
7277 int batchSize_ = DEFAULT_BATCH_SIZE,
7278 int topN_ = DEFAULT_TOP_N,
7279 const std::string& options_ = "",
7280 long timeout_ = DEFAULT_COMMAND_TIMEOUT)
7281 {
7282 MessageStream result(*this);
7283 if (_body.get().getDefaultMaxDepth())
7284 {
7285 result.maxDepth(_body.get().getDefaultMaxDepth());
7286 }
7287 result.setSubscription(_body.get().sowAndSubscribe(
7288 result.operator MessageHandler(),
7289 topic_, filter_, orderBy_,
7290 bookmark_, batchSize_, topN_,
7291 options_, timeout_, false));
7292 return result;
7293 }
7294
7296 MessageStream sowAndSubscribe(const char* topic_,
7297 const std::string& filter_ = "",
7298 const std::string& orderBy_ = "",
7299 const std::string& bookmark_ = "",
7300 int batchSize_ = DEFAULT_BATCH_SIZE,
7301 int topN_ = DEFAULT_TOP_N,
7302 const std::string& options_ = "",
7303 long timeout_ = DEFAULT_COMMAND_TIMEOUT)
7304 {
7305 MessageStream result(*this);
7306 if (_body.get().getDefaultMaxDepth())
7307 {
7308 result.maxDepth(_body.get().getDefaultMaxDepth());
7309 }
7310 result.setSubscription(_body.get().sowAndSubscribe(
7311 result.operator MessageHandler(),
7312 topic_, filter_, orderBy_,
7313 bookmark_, batchSize_, topN_,
7314 options_, timeout_, false));
7315 return result;
7316 }
7317
7342 std::string sowAndDeltaSubscribe(const MessageHandler& messageHandler_,
7343 const std::string& topic_,
7344 const std::string& filter_ = "",
7345 const std::string& orderBy_ = "",
7346 int batchSize_ = DEFAULT_BATCH_SIZE,
7347 int topN_ = DEFAULT_TOP_N,
7348 const std::string& options_ = "",
7349 long timeout_ = DEFAULT_COMMAND_TIMEOUT)
7350 {
7351 return _body.get().sowAndDeltaSubscribe(messageHandler_, topic_,
7352 filter_, orderBy_, batchSize_,
7353 topN_, options_, timeout_);
7354 }
7375 MessageStream sowAndDeltaSubscribe(const std::string& topic_,
7376 const std::string& filter_ = "",
7377 const std::string& orderBy_ = "",
7378 int batchSize_ = DEFAULT_BATCH_SIZE,
7379 int topN_ = DEFAULT_TOP_N,
7380 const std::string& options_ = "",
7381 long timeout_ = DEFAULT_COMMAND_TIMEOUT)
7382 {
7383 MessageStream result(*this);
7384 if (_body.get().getDefaultMaxDepth())
7385 {
7386 result.maxDepth(_body.get().getDefaultMaxDepth());
7387 }
7388 result.setSubscription(_body.get().sowAndDeltaSubscribe(
7389 result.operator MessageHandler(),
7390 topic_, filter_, orderBy_,
7391 batchSize_, topN_, options_,
7392 timeout_, false));
7393 return result;
7394 }
7395
7397 MessageStream sowAndDeltaSubscribe(const char* topic_,
7398 const std::string& filter_ = "",
7399 const std::string& orderBy_ = "",
7400 int batchSize_ = DEFAULT_BATCH_SIZE,
7401 int topN_ = DEFAULT_TOP_N,
7402 const std::string& options_ = "",
7403 long timeout_ = DEFAULT_COMMAND_TIMEOUT)
7404 {
7405 MessageStream result(*this);
7406 if (_body.get().getDefaultMaxDepth())
7407 {
7408 result.maxDepth(_body.get().getDefaultMaxDepth());
7409 }
7410 result.setSubscription(_body.get().sowAndDeltaSubscribe(
7411 result.operator MessageHandler(),
7412 topic_, filter_, orderBy_,
7413 batchSize_, topN_, options_,
7414 timeout_, false));
7415 return result;
7416 }
7417
7442 std::string sowAndDeltaSubscribe(const MessageHandler& messageHandler_,
7443 const std::string& topic_,
7444 long timeout_,
7445 const std::string& filter_ = "",
7446 int batchSize_ = DEFAULT_BATCH_SIZE,
7447 bool oofEnabled_ = false,
7448 bool sendEmpties_ = false,
7449 int topN_ = DEFAULT_TOP_N)
7450 {
7451 return _body.get().sowAndDeltaSubscribe(messageHandler_, topic_,
7452 timeout_, filter_, batchSize_,
7453 oofEnabled_, sendEmpties_,
7454 topN_);
7455 }
7456
7478 MessageStream sowAndDeltaSubscribe(const std::string& topic_,
7479 long timeout_,
7480 const std::string& filter_ = "",
7481 int batchSize_ = DEFAULT_BATCH_SIZE,
7482 bool oofEnabled_ = false,
7483 bool sendEmpties_ = false,
7484 int topN_ = DEFAULT_TOP_N)
7485 {
7486 MessageStream result(*this);
7487 if (_body.get().getDefaultMaxDepth())
7488 {
7489 result.maxDepth(_body.get().getDefaultMaxDepth());
7490 }
7491 result.setSubscription(_body.get().sowAndDeltaSubscribe(
7492 result.operator MessageHandler(),
7493 topic_, timeout_, filter_,
7494 batchSize_, oofEnabled_,
7495 sendEmpties_, topN_, false));
7496 return result;
7497 }
7519 MessageStream sowAndDeltaSubscribe(const char* topic_,
7520 long timeout_,
7521 const std::string& filter_ = "",
7522 int batchSize_ = DEFAULT_BATCH_SIZE,
7523 bool oofEnabled_ = false,
7524 bool sendEmpties_ = false,
7525 int topN_ = DEFAULT_TOP_N)
7526 {
7527 MessageStream result(*this);
7528 if (_body.get().getDefaultMaxDepth())
7529 {
7530 result.maxDepth(_body.get().getDefaultMaxDepth());
7531 }
7532 result.setSubscription(_body.get().sowAndDeltaSubscribe(
7533 result.operator MessageHandler(),
7534 topic_, timeout_, filter_,
7535 batchSize_, oofEnabled_,
7536 sendEmpties_, topN_, false));
7537 return result;
7538 }
7558 std::string sowDelete(const MessageHandler& messageHandler,
7559 const std::string& topic,
7560 const std::string& filter,
7561 long timeout)
7562 {
7563 return _body.get().sowDelete(messageHandler, topic, filter, timeout);
7564 }
7581 Message sowDelete(const std::string& topic_, const std::string& filter_,
7582 long timeout_ = 0)
7583 {
7584 MessageStream stream(*this);
7585 stream.timeout((unsigned int)timeout_);
7586 char buf[Message::IdentifierLength + 1];
7587 buf[Message::IdentifierLength] = 0;
7588 AMPS_snprintf(buf, Message::IdentifierLength + 1, "%lx", MessageImpl::newId());
7589 Field cid(buf);
7590 try
7591 {
7592 stream.setStatsOnly(cid);
7593 _body.get().sowDelete(stream.operator MessageHandler(), topic_, filter_, timeout_, cid);
7594 return *(stream.begin());
7595 }
7596 catch (const DisconnectedException&)
7597 {
7598 removeMessageHandler(cid);
7599 throw;
7600 }
7601 catch (const TimedOutException&)
7602 {
7603 removeMessageHandler(cid);
7604 throw;
7605 }
7606 }
7607
7612 void startTimer()
7613 {
7614 _body.get().startTimer();
7615 }
7616
7623 std::string stopTimer(const MessageHandler& messageHandler)
7624 {
7625 return _body.get().stopTimer(messageHandler);
7626 }
7627
7649 std::string sowDeleteByKeys(const MessageHandler& messageHandler_,
7650 const std::string& topic_,
7651 const std::string& keys_,
7652 long timeout_ = 0)
7653 {
7654 return _body.get().sowDeleteByKeys(messageHandler_, topic_, keys_, timeout_);
7655 }
7676 Message sowDeleteByKeys(const std::string& topic_, const std::string& keys_,
7677 long timeout_ = 0)
7678 {
7679 MessageStream stream(*this);
7680 stream.timeout((unsigned int)timeout_);
7681 char buf[Message::IdentifierLength + 1];
7682 buf[Message::IdentifierLength] = 0;
7683 AMPS_snprintf(buf, Message::IdentifierLength + 1, "%lx", MessageImpl::newId());
7684 Field cid(buf);
7685 try
7686 {
7687 stream.setStatsOnly(cid);
7688 _body.get().sowDeleteByKeys(stream.operator MessageHandler(), topic_, keys_, timeout_, cid);
7689 return *(stream.begin());
7690 }
7691 catch (const DisconnectedException&)
7692 {
7693 removeMessageHandler(cid);
7694 throw;
7695 }
7696 catch (const TimedOutException&)
7697 {
7698 removeMessageHandler(cid);
7699 throw;
7700 }
7701 }
7702
7717 std::string sowDeleteByData(const MessageHandler& messageHandler_,
7718 const std::string& topic_, const std::string& data_,
7719 long timeout_ = 0)
7720 {
7721 return _body.get().sowDeleteByData(messageHandler_, topic_, data_, timeout_);
7722 }
7723
7738 Message sowDeleteByData(const std::string& topic_, const std::string& data_,
7739 long timeout_ = 0)
7740 {
7741 MessageStream stream(*this);
7742 stream.timeout((unsigned int)timeout_);
7743 char buf[Message::IdentifierLength + 1];
7744 buf[Message::IdentifierLength] = 0;
7745 AMPS_snprintf(buf, Message::IdentifierLength + 1, "%lx", MessageImpl::newId());
7746 Field cid(buf);
7747 try
7748 {
7749 stream.setStatsOnly(cid);
7750 _body.get().sowDeleteByData(stream.operator MessageHandler(), topic_, data_, timeout_, cid);
7751 return *(stream.begin());
7752 }
7753 catch (const DisconnectedException&)
7754 {
7755 removeMessageHandler(cid);
7756 throw;
7757 }
7758 catch (const TimedOutException&)
7759 {
7760 removeMessageHandler(cid);
7761 throw;
7762 }
7763 }
7764
7768 amps_handle getHandle()
7769 {
7770 return _body.get().getHandle();
7771 }
7772
7781 void setExceptionListener(const std::shared_ptr<const ExceptionListener>& pListener_)
7782 {
7783 _body.get().setExceptionListener(pListener_);
7784 }
7785
7794 #if defined(_WIN32) || __cplusplus >= 201402L
7795 [[deprecated("Use setExceptionListener(std::shared_ptr<const ExceptionListener>&)")]]
7796 #endif
7797 void setExceptionListener(const ExceptionListener& listener_)
7798 {
7799 _body.get().setExceptionListener(listener_);
7800 }
7801
7804 const ExceptionListener& getExceptionListener(void) const
7805 {
7806 return _body.get().getExceptionListener();
7807 }
7808
7816 // type of message) from the server for the specified interval (plus a grace period),
7830 void setHeartbeat(unsigned heartbeatTime_, unsigned readTimeout_)
7831 {
7832 _body.get().setHeartbeat(heartbeatTime_, readTimeout_);
7833 }
7834
7842 // type of message) from the server for the specified interval (plus a grace period),
7854 void setHeartbeat(unsigned heartbeatTime_)
7855 {
7856 _body.get().setHeartbeat(heartbeatTime_, 2 * heartbeatTime_);
7857 }
7858
7860 #if defined(_WIN32) || __cplusplus >= 201402L
7861 [[deprecated("Use setLastChanceMessageHandler.")]]
7862 #endif
7863 void setUnhandledMessageHandler(const AMPS::MessageHandler& messageHandler)
7864 {
7865 setLastChanceMessageHandler(messageHandler);
7866 }
7867
7870 void setLastChanceMessageHandler(const AMPS::MessageHandler& messageHandler)
7871 {
7872 _body.get().setGlobalCommandTypeMessageHandler(ClientImpl::GlobalCommandTypeHandlers::LastChance,
7873 messageHandler);
7874 }
7875
7896 void setGlobalCommandTypeMessageHandler(const std::string& command_, const MessageHandler& handler_)
7897 {
7898 _body.get().setGlobalCommandTypeMessageHandler(command_, handler_);
7899 }
7900
7921 void setGlobalCommandTypeMessageHandler(const Message::Command::Type command_, const MessageHandler& handler_)
7922 {
7923 _body.get().setGlobalCommandTypeMessageHandler(command_, handler_);
7924 }
7925
7931 static const char* BOOKMARK_NOW()
7932 {
7933 return AMPS_BOOKMARK_NOW;
7934 }
7940 static const char* NOW()
7941 {
7942 return AMPS_BOOKMARK_NOW;
7943 }
7944
7950 static const char* BOOKMARK_EPOCH()
7951 {
7952 return AMPS_BOOKMARK_EPOCH;
7953 }
7954
7960 static const char* EPOCH()
7961 {
7962 return AMPS_BOOKMARK_EPOCH;
7963 }
7964
7971 static const char* BOOKMARK_MOST_RECENT()
7972 {
7973 return AMPS_BOOKMARK_RECENT;
7974 }
7975
7982 static const char* MOST_RECENT()
7983 {
7984 return AMPS_BOOKMARK_RECENT;
7985 }
7986
7993 static const char* BOOKMARK_RECENT()
7994 {
7995 return AMPS_BOOKMARK_RECENT;
7996 }
7997
7998
8004 void addConnectionStateListener(ConnectionStateListener* listener)
8005 {
8006 _body.get().addConnectionStateListener(listener);
8007 }
8008
8012 void removeConnectionStateListener(ConnectionStateListener* listener)
8013 {
8014 _body.get().removeConnectionStateListener(listener);
8015 }
8016
8019 void clearConnectionStateListeners()
8020 {
8021 _body.get().clearConnectionStateListeners();
8022 }
8023
8049 std::string executeAsync(Command& command_, MessageHandler handler_)
8050 {
8051 return _body.get().executeAsync(command_, handler_);
8052 }
8053
8083 std::string executeAsyncNoResubscribe(Command& command_,
8084 MessageHandler handler_)
8085 {
8086 std::string id;
8087 try
8088 {
8089 if (command_.isSubscribe())
8090 {
8091 Message& message = command_.getMessage();
8092 Field subId = message.getSubscriptionId();
8093 bool useExistingHandler = !subId.empty() && !message.getOptions().empty() && message.getOptions().contains("replace", 7);
8094 if (useExistingHandler)
8095 {
8096 MessageHandler existingHandler;
8097 if (_body.get()._routes.getRoute(subId, existingHandler))
8098 {
8099 // we found an existing handler.
8100 _body.get().executeAsync(command_, existingHandler, false);
8101 return id; // empty string indicates existing
8102 }
8103 }
8104 }
8105 id = _body.get().executeAsync(command_, handler_, false);
8106 }
8107 catch (const DisconnectedException&)
8108 {
8109 removeMessageHandler(command_.getMessage().getCommandId());
8110 if (command_.isSubscribe())
8111 {
8112 removeMessageHandler(command_.getMessage().getSubscriptionId());
8113 }
8114 if (command_.isSow())
8115 {
8116 removeMessageHandler(command_.getMessage().getQueryID());
8117 }
8118 throw;
8119 }
8120 return id;
8121 }
8122
8135 MessageStream execute(Command& command_);
8136
8145 void ack(Field& topic_, Field& bookmark_, const char* options_ = NULL)
8146 {
8147 _body.get().ack(topic_, bookmark_, options_);
8148 }
8149
8157 void ack(Message& message_, const char* options_ = NULL)
8158 {
8159 _body.get().ack(message_.getTopic(), message_.getBookmark(), options_);
8160 }
8169 void ack(const std::string& topic_, const std::string& bookmark_,
8170 const char* options_ = NULL)
8171 {
8172 _body.get().ack(Field(topic_.data(), topic_.length()), Field(bookmark_.data(), bookmark_.length()), options_);
8173 }
8174
8180 void ackDeferredAutoAck(Field& topic_, Field& bookmark_, const char* options_ = NULL)
8181 {
8182 _body.get()._ack(topic_, bookmark_, options_);
8183 }
8193 void flushAcks(void)
8194 {
8195 _body.get().flushAcks();
8196 }
8197
8202 bool getAutoAck(void) const
8203 {
8204 return _body.get().getAutoAck();
8205 }
8212 void setAutoAck(bool isAutoAckEnabled_)
8213 {
8214 _body.get().setAutoAck(isAutoAckEnabled_);
8215 }
8220 unsigned getAckBatchSize(void) const
8221 {
8222 return _body.get().getAckBatchSize();
8223 }
8230 void setAckBatchSize(const unsigned ackBatchSize_)
8231 {
8232 _body.get().setAckBatchSize(ackBatchSize_);
8233 }
8234
8241 int getAckTimeout(void) const
8242 {
8243 return _body.get().getAckTimeout();
8244 }
8253 void setAckTimeout(const int ackTimeout_)
8254 {
8255 if (!ackTimeout_ && _body.get().getAckBatchSize() > 1)
8256 {
8257 throw UsageException("Ack timeout must be > 0 when ack batch size > 1");
8258 }
8259 _body.get().setAckTimeout(ackTimeout_);
8260 }
8261
8262
8271 void setRetryOnDisconnect(bool isRetryOnDisconnect_)
8272 {
8273 _body.get().setRetryOnDisconnect(isRetryOnDisconnect_);
8274 }
8275
8280 bool getRetryOnDisconnect(void) const
8281 {
8282 return _body.get().getRetryOnDisconnect();
8283 }
8284
8289 void setDefaultMaxDepth(unsigned maxDepth_)
8290 {
8291 _body.get().setDefaultMaxDepth(maxDepth_);
8292 }
8293
8298 unsigned getDefaultMaxDepth(void) const
8299 {
8300 return _body.get().getDefaultMaxDepth();
8301 }
8302
8309 void setTransportFilterFunction(amps_transport_filter_function filter_,
8310 void* userData_)
8311 {
8312 return _body.get().setTransportFilterFunction(filter_, userData_);
8313 }
8314
8323 void setThreadCreatedCallback(amps_thread_created_callback callback_,
8324 void* userData_)
8325 {
8326 return _body.get().setThreadCreatedCallback(callback_, userData_);
8327 }
8328
8335 void setPublishBatching(size_t batchSize_, amps_uint64_t batchTimeoutMillis_)
8336 {
8337 return _body.get().setPublishBatching(batchSize_, batchTimeoutMillis_);
8338 }
8339
8345 void deferredExecution(DeferredExecutionFunc func_, void* userData_)
8346 {
8347 _body.get().deferredExecution(func_, userData_);
8348 }
8352 };
8353
8354 inline void
8355 ClientImpl::lastChance(AMPS::Message& message)
8356 {
8357 AMPS_CALL_EXCEPTION_WRAPPER(_globalCommandTypeHandlers[GlobalCommandTypeHandlers::LastChance].invoke(message));
8358 }
8359
8360 inline unsigned
8361 ClientImpl::persistedAck(AMPS::Message& message)
8362 {
8363 unsigned deliveries = 0;
8364 try
8365 {
8366 /*
8367 * Best Practice: If you don't care about the dupe acks that
8368 * occur during failover or rapid disconnect/reconnect, then just
8369 * ignore them. We could discard each duplicate from the
8370 * persisted store, but the storage costs of doing 1 record
8371 * discards is heavy. In most scenarios we'll just quickly blow
8372 * through the duplicates and get back to processing the
8373 * non-dupes.
8374 */
8375 const char* data = NULL;
8376 size_t len = 0;
8377 const char* status = NULL;
8378 size_t statusLen = 0;
8379 amps_handle messageHandle = message.getMessage();
8380 const size_t NotEntitled = 12, Duplicate = 9, Failure = 7;
8381 amps_message_get_field_value(messageHandle, AMPS_Reason, &data, &len);
8382 amps_message_get_field_value(messageHandle, AMPS_Status, &status, &statusLen);
8383 if (len == NotEntitled || len == Duplicate ||
8384 (statusLen == Failure && status[0] == 'f'))
8385 {
8386 if (_failedWriteHandler)
8387 {
8388 if (_publishStore.isValid())
8389 {
8390 amps_uint64_t sequence =
8391 amps_message_get_field_uint64(messageHandle, AMPS_Sequence);
8392 FailedWriteStoreReplayer replayer(this, data, len);
8393 AMPS_CALL_EXCEPTION_WRAPPER(_publishStore.replaySingle(
8394 replayer, sequence));
8395 }
8396 else // Call the handler with what little we have
8397 {
8398 Message emptyMessage;
8399 emptyMessage.setSequence(message.getSequence());
8400 AMPS_CALL_EXCEPTION_WRAPPER(
8401 _failedWriteHandler->failedWrite(emptyMessage,
8402 data, len));
8403 }
8404 ++deliveries;
8405 }
8406 }
8407 if (_publishStore.isValid())
8408 {
8409 // Ack for publisher will have sequence while
8410 // ack for bookmark subscribe won't
8411 amps_uint64_t seq = amps_message_get_field_uint64(messageHandle,
8412 AMPS_Sequence);
8413 if (seq > 0)
8414 {
8415 ++deliveries;
8416 AMPS_CALL_EXCEPTION_WRAPPER(_publishStore.discardUpTo(seq));
8417 }
8418 }
8419
8420 if (!deliveries && _bookmarkStore.isValid())
8421 {
8422 amps_message_get_field_value(messageHandle, AMPS_SubscriptionId,
8423 &data, &len);
8424 if (len > 0)
8425 {
8426 Message::Field subId(data, len);
8427 const char* bookmarkData = NULL;
8428 size_t bookmarkLen = 0;
8429 amps_message_get_field_value(messageHandle,
8430 AMPS_Bookmark,
8431 &bookmarkData,
8432 &bookmarkLen);
8433 // Everything is there and not unsubscribed AC-912
8434 if (bookmarkLen > 0 && _routes.hasRoute(subId))
8435 {
8436 ++deliveries;
8437 _bookmarkStore.persisted(subId, Message::Field(bookmarkData, bookmarkLen));
8438 }
8439 }
8440 }
8441 }
8442 catch (std::exception& ex)
8443 {
8444 AMPS_UNHANDLED_EXCEPTION(ex);
8445 }
8446 return deliveries;
8447 }
8448
8449 inline unsigned
8450 ClientImpl::processedAck(Message& message)
8451 {
8452 unsigned deliveries = 0;
8453 AckResponse ack;
8454 const char* data = NULL;
8455 size_t len = 0;
8456 amps_handle messageHandle = message.getMessage();
8457 amps_message_get_field_value(messageHandle, AMPS_CommandId, &data, &len);
8458 Lock<Mutex> l(_lock);
8459 if (data && len)
8460 {
8461 Lock<Mutex> guard(_ackMapLock);
8462 AckMap::iterator i = _ackMap.find(std::string(data, len));
8463 if (i != _ackMap.end())
8464 {
8465 ++deliveries;
8466 ack = i->second;
8467 _ackMap.erase(i);
8468 }
8469 }
8470 if (deliveries)
8471 {
8472 amps_message_get_field_value(messageHandle, AMPS_Status, &data, &len);
8473 ack.setStatus(data, len);
8474 amps_message_get_field_value(messageHandle, AMPS_Reason, &data, &len);
8475 ack.setReason(data, len);
8476 amps_message_get_field_value(messageHandle, AMPS_UserId, &data, &len);
8477 ack.setUsername(data, len);
8478 amps_message_get_field_value(messageHandle, AMPS_Password, &data, &len);
8479 ack.setPassword(data, len);
8480 amps_message_get_field_value(messageHandle, AMPS_Version, &data, &len);
8481 ack.setServerVersion(data, len);
8482 amps_message_get_field_value(messageHandle, AMPS_Options, &data, &len);
8483 ack.setOptions(data, len);
8484 // This sets bookmark, nameHashValue, and sequenceNo
8485 ack.setBookmark(message.getBookmark());
8486 ack.setResponded();
8487 _lock.signalAll();
8488 }
8489 return deliveries;
8490 }
8491
8492 inline void
8493 ClientImpl::checkAndSendHeartbeat(bool force)
8494 {
8495 if (force || _heartbeatTimer.check())
8496 {
8497 _heartbeatTimer.start();
8498 try
8499 {
8500 sendWithoutRetry(_beatMessage);
8501 }
8502 catch (const AMPSException&)
8503 {
8504 ;
8505 }
8506 }
8507 }
8508
8509 inline ConnectionInfo ClientImpl::getConnectionInfo() const
8510 {
8511 ConnectionInfo info;
8512 std::ostringstream writer;
8513
8514 info["client.uri"] = _lastUri;
8515 info["client.name"] = _name;
8516 info["client.username"] = _username;
8517 if (_publishStore.isValid())
8518 {
8519 writer << _publishStore.unpersistedCount();
8520 info["publishStore.unpersistedCount"] = writer.str();
8521 writer.clear();
8522 writer.str("");
8523 }
8524
8525 return info;
8526 }
8527
8528 inline std::string ClientImpl::logon(long timeout_, Authenticator& authenticator_,
8529 const char* options_)
8530 {
8531 Lock<Mutex> l(_lock);
8532 std::string cmdId = _logon(timeout_, authenticator_, options_);
8533 // Resubscribe
8534 if (_subscriptionManager)
8535 {
8536 {
8537 Client wrapper(this, false);
8538 // Have to release the lock here or receive thread can't
8539 // invoke the message handler.
8540 Unlock<Mutex> unlock(_lock);
8541 _subscriptionManager->resubscribe(wrapper);
8542 }
8543 broadcastConnectionStateChanged(ConnectionStateListener::Resubscribed);
8544 }
8545 return cmdId;
8546 }
8547
8548 inline amps_result
8549 ClientImpl::ClientImplMessageHandler(amps_handle messageHandle_, void* userData_)
8550 {
8551 const unsigned SOWMask = Message::Command::SOW | Message::Command::GroupBegin | Message::Command::GroupEnd;
8552 const unsigned PublishMask = Message::Command::OOF | Message::Command::Publish | Message::Command::DeltaPublish;
8553 ClientImpl* me = (ClientImpl*) userData_;
8554 AMPS_CALL_EXCEPTION_WRAPPER_2(me, me->processDeferredExecutions());
8555 if (!messageHandle_)
8556 {
8557 if (me->_queueAckTimeout)
8558 {
8559 me->checkQueueAcks();
8560 }
8561 me->checkAndSendHeartbeat();
8562 return AMPS_E_OK;
8563 }
8564
8565 me->_readMessage.replace(messageHandle_);
8566 Message& message = me->_readMessage;
8567 Message::Command::Type commandType = message.getCommandEnum();
8568 if (commandType & SOWMask)
8569 {
8570 #if 0 // Not currently implemented, to avoid an extra branch in delivery
8571 // A small cheat here to get the right handler, using knowledge of the
8572 // Command values of SOW (8), GroupBegin (8192), and GroupEnd (16384)
8573 // and their GlobalCommandTypeHandlers values 1, 2, 3.
8574 AMPS_CALL_EXCEPTION_WRAPPER_2(me,
8575 me->_globalCommandTypeHandlers[1 + (commandType / 8192)].invoke(message));
8576 #endif
8577 AMPS_CALL_EXCEPTION_WRAPPER_2(me, me->_routes.deliverData(message,
8578 message.getQueryID()));
8579 }
8580 else if (commandType & PublishMask)
8581 {
8582 #if 0 // Not currently implemented, to avoid an extra branch in delivery
8583 AMPS_CALL_EXCEPTION_WRAPPER_2(me,
8584 me->_globalCommandTypeHandlers[(commandType == Message::Command::Publish ?
8585 GlobalCommandTypeHandlers::Publish :
8586 GlobalCommandTypeHandlers::OOF)].invoke(message));
8587 #endif
8588 const char* subIds = NULL;
8589 size_t subIdsLen = 0;
8590 // Publish command, send to subscriptions
8591 amps_message_get_field_value(messageHandle_, AMPS_SubscriptionIds,
8592 &subIds, &subIdsLen);
8593 size_t subIdCount = me->_routes.parseRoutes(AMPS::Field(subIds, subIdsLen), me->_routeCache);
8594 for (size_t i = 0; i < subIdCount; ++i)
8595 {
8596 MessageRouter::RouteCache::value_type& lookupResult = me->_routeCache[i];
8597 MessageHandler& handler = lookupResult.handler;
8598 if (handler.isValid())
8599 {
8600 amps_message_set_field_value(messageHandle_,
8601 AMPS_SubscriptionId,
8602 subIds + lookupResult.idOffset,
8603 lookupResult.idLength);
8604 Message::Field bookmark = message.getBookmark();
8605 bool isMessageQueue = message.getLeasePeriod().len() != 0;
8606 bool isAutoAck = me->_isAutoAckEnabled;
8607
8608 if (!isMessageQueue && !bookmark.empty() &&
8609 me->_bookmarkStore.isValid())
8610 {
8611 if (me->_bookmarkStore.isDiscarded(me->_readMessage))
8612 {
8613 //Call duplicate message handler in handlers map
8614 if (me->_globalCommandTypeHandlers[GlobalCommandTypeHandlers::DuplicateMessage].isValid())
8615 {
8616 AMPS_CALL_EXCEPTION_WRAPPER_2(me, me->_globalCommandTypeHandlers[GlobalCommandTypeHandlers::DuplicateMessage].invoke(message));
8617 }
8618 }
8619 else
8620 {
8621 me->_bookmarkStore.log(me->_readMessage);
8622 AMPS_CALL_EXCEPTION_WRAPPER_2(me,
8623 handler.invoke(message));
8624 }
8625 }
8626 else
8627 {
8628 if (isMessageQueue && isAutoAck)
8629 {
8630 try
8631 {
8632 AMPS_CALL_EXCEPTION_WRAPPER_STREAM_FULL_2(me, handler.invoke(message));
8633 if (!message.getIgnoreAutoAck())
8634 {
8635 AMPS_CALL_EXCEPTION_WRAPPER_2(me,
8636 me->_ack(message.getTopic(), message.getBookmark()));
8637 }
8638 }
8639 catch (std::exception& ex)
8640 {
8641 if (!message.getIgnoreAutoAck())
8642 {
8643 AMPS_CALL_EXCEPTION_WRAPPER_2(me,
8644 me->_ack(message.getTopic(), message.getBookmark(), "cancel"));
8645 }
8646 AMPS_UNHANDLED_EXCEPTION_2(me, ex);
8647 }
8648 }
8649 else
8650 {
8651 AMPS_CALL_EXCEPTION_WRAPPER_2(me,
8652 handler.invoke(message));
8653 }
8654 }
8655 }
8656 else
8657 {
8658 me->lastChance(message);
8659 }
8660 } // for (subidsEnd)
8661 }
8662 else if (commandType == Message::Command::Ack)
8663 {
8664 unsigned ackType = message.getAckTypeEnum();
8665 unsigned deliveries = 0U;
8666 switch (ackType)
8667 {
8668 case Message::AckType::Persisted:
8669 deliveries += me->persistedAck(message);
8670 break;
8671 case Message::AckType::Processed: // processed
8672 deliveries += me->processedAck(message);
8673 break;
8674 }
8675 MessageHandler ackHandler = me->_globalCommandTypeHandlers[GlobalCommandTypeHandlers::Ack];
8676 if (ackHandler.isValid())
8677 {
8678 AMPS_CALL_EXCEPTION_WRAPPER_2(me, ackHandler.invoke(message));
8679 ++deliveries;
8680 }
8681 AMPS_CALL_EXCEPTION_WRAPPER_2(me, deliveries += me->_routes.deliverAck(message, ackType));
8682 if (deliveries == 0)
8683 {
8684 me->lastChance(message);
8685 }
8686 }
8687 else if (commandType == Message::Command::Heartbeat)
8688 {
8689 AMPS_CALL_EXCEPTION_WRAPPER_2(me,
8690 me->_globalCommandTypeHandlers[GlobalCommandTypeHandlers::Heartbeat].invoke(message));
8691 if (me->_heartbeatTimer.getTimeout() != 0.0) // -V550
8692 {
8693 me->checkAndSendHeartbeat(true);
8694 }
8695 else
8696 {
8697 me->lastChance(message);
8698 }
8699 return AMPS_E_OK;
8700 }
8701 else if (!message.getCommandId().empty())
8702 {
8703 unsigned deliveries = 0U;
8704 try
8705 {
8706 while (me->_connected) // Keep sending heartbeats when stream is full
8707 {
8708 try
8709 {
8710 deliveries = me->_routes.deliverData(message, message.getCommandId());
8711 break;
8712 }
8713 #ifdef _WIN32
8714 catch (MessageStreamFullException&)
8715 #else
8716 catch (MessageStreamFullException& msfEx_)
8717 #endif
8718 {
8719 try
8720 {
8721 me->checkAndSendHeartbeat(false);
8722 }
8723 #ifdef _WIN32
8724 catch (std::exception&)
8725 #else
8726 catch (std::exception& stdEx_)
8727 #endif
8728 {
8729 ;
8730 }
8731 }
8732 }
8733 }
8734 catch (std::exception& stdEx_)
8735 {
8736 try
8737 {
8738 me->_exceptionListener->exceptionThrown(stdEx_);
8739 }
8740 catch (...)
8741 {
8742 ;
8743 }
8744 }
8745 if (deliveries == 0)
8746 {
8747 me->lastChance(message);
8748 }
8749 }
8750 me->checkAndSendHeartbeat();
8751 return AMPS_E_OK;
8752 }
8753
8754 inline void
8755 ClientImpl::ClientImplPreDisconnectHandler(amps_handle /*client*/, unsigned failedConnectionVersion, void* userData)
8756 {
8757 ClientImpl* me = (ClientImpl*) userData;
8758 //Client wrapper(me);
8759 // Go ahead and signal any waiters if they are around...
8760 me->clearAcks(failedConnectionVersion);
8761 }
8762
8763 inline amps_result
8764 ClientImpl::ClientImplDisconnectHandler(amps_handle /*client*/, void* userData)
8765 {
8766 ClientImpl* me = (ClientImpl*) userData;
8767 Lock<Mutex> l(me->_lock);
8768 Client wrapper(me, false);
8769 if (me->_connected)
8770 {
8771 me->broadcastConnectionStateChanged(ConnectionStateListener::Disconnected);
8772 }
8773 me->captureSubscriptionManagerGenerationCount();
8774 bool retryInProgress = false;
8775 try
8776 {
8777 me->_connected = false;
8778 me->_lock.signalAll();
8779 // Have to release the lock here or receive thread can't
8780 // invoke the message handler.
8781 Unlock<Mutex> unlock(me->_lock);
8782 me->_disconnectHandler.invoke(wrapper);
8783 }
8784 #ifdef _WIN32
8785 catch (const RetryOperationException&)
8786 #else
8787 catch (const RetryOperationException& ex)
8788 #endif
8789 {
8790 retryInProgress = true;
8791 }
8792 catch (const std::exception& ex)
8793 {
8794 AMPS_UNHANDLED_EXCEPTION_2(me, ex);
8795 }
8796 me->_lock.signalAll();
8797
8798 if (!me->_connected)
8799 {
8800 if (retryInProgress)
8801 {
8802 AMPS_UNHANDLED_EXCEPTION_2(me, RetryOperationException("Reconnect in progress."));
8803 }
8804 else
8805 {
8806 me->broadcastConnectionStateChanged(ConnectionStateListener::Shutdown);
8807 AMPS_UNHANDLED_EXCEPTION_2(me, DisconnectedException("Reconnect failed."));
8808 }
8809 return AMPS_E_DISCONNECTED;
8810 }
8811 return AMPS_E_OK;
8812 }
8813
8814 inline const char*
8815 ClientImpl::ClientImplGetHttpPreflightMessage(void* userData_)
8816 {
8817 ClientImpl* me = (ClientImpl*)userData_;
8818 std::ostringstream os;
8819 // [transport]://[user[:password]@][host]:port[/path][?uri_params]
8820 // firstColon is after transport
8821 size_t firstColon = me->_lastUri.find(':');
8822 // pathEnd is start of uri_params or npos
8823 size_t pathEnd = me->_lastUri.find('?');
8824 // lastColon separates host and port, last before pathEnd
8825 size_t lastColon = me->_lastUri.rfind(':', pathEnd);
8826 // at ends user/password and precedes host
8827 size_t at = me->_lastUri.rfind('@', lastColon);
8828 // hostStart is either after at or following firstColon ://
8829 size_t hostStart = at == std::string::npos ? firstColon + 3 : at + 1;
8830 size_t hostLen = lastColon - hostStart;
8831 // pathStart follows port
8832 size_t pathStart = me->_lastUri.find('/', lastColon);
8833 size_t pathLen = pathEnd;
8834 if (pathEnd != std::string::npos)
8835 {
8836 pathLen = pathEnd - pathStart;
8837 }
8838 os << "GET " << me->_lastUri.substr(pathStart, pathLen)
8839 << " HTTP/1.1\r\nHost: " << me->_lastUri.substr(hostStart, hostLen)
8840 << "\r\nConnection: upgrade\r\nUpgrade: "
8841 << me->_lastUri.substr(0, firstColon) << "\r\n";
8842 for (auto& header : me->_httpPreflightHeaders)
8843 {
8844 os << header << "\r\n";
8845 }
8846 os << "\r\n";
8847 me->_preflightMessage = os.str();
8848 return me->_preflightMessage.c_str();
8849 }
8850
8851 class FIX
8852 {
8853 const char* _data;
8854 size_t _len;
8855 char _fieldSep;
8856 public:
8857 class iterator
8858 {
8859 const char* _data;
8860 size_t _len;
8861 size_t _pos;
8862 char _fieldSep;
8863 iterator(const char* data_, size_t len_, size_t pos_, char fieldSep_)
8864 : _data(data_), _len(len_), _pos(pos_), _fieldSep(fieldSep_)
8865 {
8866 while (_pos != _len && _data[_pos] == _fieldSep)
8867 {
8868 ++_pos;
8869 }
8870 }
8871 public:
8872 typedef void* difference_type;
8873 typedef std::forward_iterator_tag iterator_category;
8874 typedef std::pair<Message::Field, Message::Field> value_type;
8875 typedef value_type* pointer;
8876 typedef value_type& reference;
8877 bool operator==(const iterator& rhs) const
8878 {
8879 return _pos == rhs._pos;
8880 }
8881 bool operator!=(const iterator& rhs) const
8882 {
8883 return _pos != rhs._pos;
8884 }
8885 iterator& operator++()
8886 {
8887 // Skip through the data
8888 while (_pos != _len && _data[_pos] != _fieldSep)
8889 {
8890 ++_pos;
8891 }
8892 // Skip through any field separators
8893 while (_pos != _len && _data[_pos] == _fieldSep)
8894 {
8895 ++_pos;
8896 }
8897 return *this;
8898 }
8899
8900 value_type operator*() const
8901 {
8902 value_type result;
8903 size_t i = _pos, keyLength = 0, valueStart = 0, valueLength = 0;
8904 for (; i < _len && _data[i] != '='; ++i)
8905 {
8906 ++keyLength;
8907 }
8908
8909 result.first.assign(_data + _pos, keyLength);
8910
8911 if (i < _len && _data[i] == '=')
8912 {
8913 ++i;
8914 valueStart = i;
8915 for (; i < _len && _data[i] != _fieldSep; ++i)
8916 {
8917 valueLength++;
8918 }
8919 }
8920 result.second.assign(_data + valueStart, valueLength);
8921 return result;
8922 }
8923
8924 friend class FIX;
8925 };
8926 class reverse_iterator
8927 {
8928 const char* _data;
8929 size_t _len;
8930 const char* _pos;
8931 char _fieldSep;
8932 public:
8933 typedef std::pair<Message::Field, Message::Field> value_type;
8934 reverse_iterator(const char* data, size_t len, const char* pos, char fieldsep)
8935 : _data(data), _len(len), _pos(pos), _fieldSep(fieldsep)
8936 {
8937 if (_pos)
8938 {
8939 // skip past meaningless trailing fieldseps
8940 while (_pos >= _data && *_pos == _fieldSep)
8941 {
8942 --_pos;
8943 }
8944 while (_pos > _data && *_pos != _fieldSep)
8945 {
8946 --_pos;
8947 }
8948 // if we stopped before the 0th character, it's because
8949 // it's a field sep. advance one to point to the first character
8950 // of a key.
8951 if (_pos > _data || (_pos == _data && *_pos == _fieldSep))
8952 {
8953 ++_pos;
8954 }
8955 if (_pos < _data)
8956 {
8957 _pos = 0;
8958 }
8959 }
8960 }
8961 bool operator==(const reverse_iterator& rhs) const
8962 {
8963 return _pos == rhs._pos;
8964 }
8965 bool operator!=(const reverse_iterator& rhs) const
8966 {
8967 return _pos != rhs._pos;
8968 }
8969 reverse_iterator& operator++()
8970 {
8971 if (_pos == _data)
8972 {
8973 _pos = 0;
8974 }
8975 else
8976 {
8977 // back up 1 to a field separator
8978 --_pos;
8979 // keep backing up through field separators
8980 while (_pos >= _data && *_pos == _fieldSep)
8981 {
8982 --_pos;
8983 }
8984 // now back up to the beginning of this field
8985 while (_pos > _data && *_pos != _fieldSep)
8986 {
8987 --_pos;
8988 }
8989 if (_pos > _data || (_pos == _data && *_pos == _fieldSep))
8990 {
8991 ++_pos;
8992 }
8993 if (_pos < _data)
8994 {
8995 _pos = 0;
8996 }
8997 }
8998 return *this;
8999 }
9000 value_type operator*() const
9001 {
9002 value_type result;
9003 size_t keyLength = 0, valueStart = 0, valueLength = 0;
9004 size_t i = (size_t)(_pos - _data);
9005 for (; i < _len && _data[i] != '='; ++i)
9006 {
9007 ++keyLength;
9008 }
9009 result.first.assign(_pos, keyLength);
9010 if (i < _len && _data[i] == '=')
9011 {
9012 ++i;
9013 valueStart = i;
9014 for (; i < _len && _data[i] != _fieldSep; ++i)
9015 {
9016 valueLength++;
9017 }
9018 }
9019 result.second.assign(_data + valueStart, valueLength);
9020 return result;
9021 }
9022 };
9023 FIX(const Message::Field& data, char fieldSeparator = 1)
9024 : _data(data.data()), _len(data.len()),
9025 _fieldSep(fieldSeparator)
9026 {
9027 }
9028
9029 FIX(const char* data, size_t len, char fieldSeparator = 1)
9030 : _data(data), _len(len), _fieldSep(fieldSeparator)
9031 {
9032 }
9033
9034 iterator begin() const
9035 {
9036 return iterator(_data, _len, 0, _fieldSep);
9037 }
9038 iterator end() const
9039 {
9040 return iterator(_data, _len, _len, _fieldSep);
9041 }
9042
9043
9044 reverse_iterator rbegin() const
9045 {
9046 return reverse_iterator(_data, _len, _data + (_len - 1), _fieldSep);
9047 }
9048
9049 reverse_iterator rend() const
9050 {
9051 return reverse_iterator(_data, _len, 0, _fieldSep);
9052 }
9053 };
9054
9055
9068
9069 template <class T>
9070 class _FIXBuilder
9071 {
9072 std::stringstream _data;
9073 char _fs;
9074 public:
9080 _FIXBuilder(char fieldSep_ = (char)1) : _fs(fieldSep_) {;}
9081
9089 void append(const T& tag, const char* value, size_t offset, size_t length)
9090 {
9091 _data << tag << '=';
9092 _data.write(value + offset, (std::streamsize)length);
9093 _data << _fs;
9094 }
9100 void append(const T& tag, const std::string& value)
9101 {
9102 _data << tag << '=' << value << _fs;
9103 }
9104
9107 std::string getString() const
9108 {
9109 return _data.str();
9110 }
9111 operator std::string() const
9112 {
9113 return _data.str();
9114 }
9115
9117 void reset()
9118 {
9119 _data.str(std::string());
9120 }
9121 };
9122
9126
9127 typedef _FIXBuilder<int> FIXBuilder;
9128
9132
9133 typedef _FIXBuilder<std::string> NVFIXBuilder;
9134
9135
9143
9144 class FIXShredder
9145 {
9146 char _fs;
9147 public:
9152 FIXShredder(char fieldSep_ = (char)1) : _fs(fieldSep_) {;}
9153
9156 typedef std::map<Message::Field, Message::Field> map_type;
9157
9163 map_type toMap(const Message::Field& data)
9164 {
9165 FIX fix(data, _fs);
9166 map_type retval;
9167 for (FIX::iterator a = fix.begin(); a != fix.end(); ++a)
9168 {
9169 retval.insert(*a);
9170 }
9171
9172 return retval;
9173 }
9174 };
9175
9176 #define AMPS_MESSAGE_STREAM_CACHE_MAX 128
9177 class MessageStreamImpl : public AMPS::RefBody, AMPS::ConnectionStateListener
9178 {
9179 Mutex _lock;
9180 std::deque<Message> _q;
9181 std::deque<Message> _cache;
9182 std::string _commandId;
9183 std::string _subId;
9184 std::string _queryId;
9185 Client _client;
9186 unsigned _timeout;
9187 unsigned _maxDepth;
9188 unsigned _requestedAcks;
9189 size_t _cacheMax;
9190 Message::Field _previousTopic;
9191 Message::Field _previousBookmark;
9192 typedef enum : unsigned int { Unset = 0x0, Running = 0x10, Subscribe = 0x11, SOWOnly = 0x12, AcksOnly = 0x13, Conflate = 0x14, Closed = 0x1, Disconnected = 0x2 } State;
9193 #if __cplusplus >= 201100L || _MSC_VER >= 1900
9194 std::atomic<State> _state;
9195 #else
9196 volatile State _state;
9197 #endif
9198 typedef std::map<std::string, Message*> SOWKeyMap;
9199 SOWKeyMap _sowKeyMap;
9200 public:
9201 MessageStreamImpl(const Client& client_)
9202 : _client(client_),
9203 _timeout(0),
9204 _maxDepth((unsigned)~0),
9205 _requestedAcks(0),
9206 _cacheMax(AMPS_MESSAGE_STREAM_CACHE_MAX),
9207 _state(Unset)
9208 {
9209 if (_client.isValid())
9210 {
9211 _client.addConnectionStateListener(this);
9212 }
9213 }
9214
9215 MessageStreamImpl(ClientImpl* client_)
9216 : _client(client_, false),
9217 _timeout(0),
9218 _maxDepth((unsigned)~0),
9219 _requestedAcks(0),
9220 _state(Unset)
9221 {
9222 if (_client.isValid())
9223 {
9224 _client.addConnectionStateListener(this);
9225 }
9226 }
9227
9228 ~MessageStreamImpl()
9229 {
9230 }
9231
9232 virtual void destroy()
9233 {
9234 try
9235 {
9236 close();
9237 }
9238 catch (std::exception& e)
9239 {
9240 try
9241 {
9242 if (_client.isValid())
9243 {
9244 _client.getExceptionListener().exceptionThrown(e);
9245 }
9246 }
9247 catch (...) {/*Ignore exception listener exceptions*/} // -V565 // -V5002
9248 }
9249 if (_client.isValid())
9250 {
9251 _client.removeConnectionStateListener(this);
9252 Client c = _client;
9253 _client = Client((ClientImpl*)NULL);
9254 c.deferredExecution(MessageStreamImpl::destroyer, this);
9255 }
9256 else
9257 {
9258 delete this;
9259 }
9260 }
9261
9262 static void destroyer(void* vpMessageStreamImpl_)
9263 {
9264 delete ((MessageStreamImpl*)vpMessageStreamImpl_);
9265 }
9266
9267 void setSubscription(const std::string& subId_,
9268 const std::string& commandId_ = "",
9269 const std::string& queryId_ = "")
9270 {
9271 Lock<Mutex> lock(_lock);
9272 _subId = subId_;
9273 if (!commandId_.empty() && commandId_ != subId_)
9274 {
9275 _commandId = commandId_;
9276 }
9277 if (!queryId_.empty() && queryId_ != subId_ && queryId_ != commandId_)
9278 {
9279 _queryId = queryId_;
9280 }
9281 // It's possible to disconnect between creation/registration and here.
9282 if (Disconnected == _state)
9283 {
9284 return;
9285 }
9286 assert(Unset == _state);
9287 _state = Subscribe;
9288 }
9289
9290 void setSOWOnly(const std::string& commandId_,
9291 const std::string& queryId_ = "")
9292 {
9293 Lock<Mutex> lock(_lock);
9294 _commandId = commandId_;
9295 if (!queryId_.empty() && queryId_ != commandId_)
9296 {
9297 _queryId = queryId_;
9298 }
9299 // It's possible to disconnect between creation/registration and here.
9300 if (Disconnected == _state)
9301 {
9302 return;
9303 }
9304 assert(Unset == _state);
9305 _state = SOWOnly;
9306 }
9307
9308 void setStatsOnly(const std::string& commandId_,
9309 const std::string& queryId_ = "")
9310 {
9311 Lock<Mutex> lock(_lock);
9312 _commandId = commandId_;
9313 if (!queryId_.empty() && queryId_ != commandId_)
9314 {
9315 _queryId = queryId_;
9316 }
9317 // It's possible to disconnect between creation/registration and here.
9318 if (Disconnected == _state)
9319 {
9320 return;
9321 }
9322 assert(Unset == _state);
9323 _state = AcksOnly;
9324 _requestedAcks = Message::AckType::Stats;
9325 }
9326
9327 void setAcksOnly(const std::string& commandId_, unsigned acks_)
9328 {
9329 Lock<Mutex> lock(_lock);
9330 _commandId = commandId_;
9331 // It's possible to disconnect between creation/registration and here.
9332 if (Disconnected == _state)
9333 {
9334 return;
9335 }
9336 assert(Unset == _state);
9337 _state = AcksOnly;
9338 _requestedAcks = acks_;
9339 }
9340
9341 void connectionStateChanged(ConnectionStateListener::State state_)
9342 {
9343 Lock<Mutex> lock(_lock);
9344 if (state_ == AMPS::ConnectionStateListener::Disconnected)
9345 {
9346 _state = Disconnected;
9347 close();
9348 }
9349 else if (state_ == AMPS::ConnectionStateListener::Connected
9350 && _commandId.empty()
9351 && _subId.empty()
9352 && _queryId.empty())
9353 {
9354 // AC-1331 Reconnect before command was sent, so Unset
9355 _state = Unset;
9356 }
9357 _lock.signalAll();
9358 }
9359
9360 void timeout(unsigned timeout_)
9361 {
9362 _timeout = timeout_;
9363 }
9364 void conflate(void)
9365 {
9366 if (_state == Subscribe)
9367 {
9368 _state = Conflate;
9369 }
9370 }
9371 void maxDepth(unsigned maxDepth_)
9372 {
9373 if (maxDepth_)
9374 {
9375 _maxDepth = maxDepth_;
9376 }
9377 else
9378 {
9379 _maxDepth = (unsigned)~0;
9380 }
9381 }
9382 unsigned getMaxDepth(void) const
9383 {
9384 return _maxDepth;
9385 }
9386 unsigned getDepth(void) const
9387 {
9388 return (unsigned)(_q.size());
9389 }
9390
9391 bool next(Message& current_)
9392 {
9393 Lock<Mutex> lock(_lock);
9394 if (!_previousTopic.empty() && !_previousBookmark.empty())
9395 {
9396 try
9397 {
9398 if (_client.isValid())
9399 {
9400 _client.ackDeferredAutoAck(_previousTopic, _previousBookmark);
9401 }
9402 }
9403 #ifdef _WIN32
9404 catch (AMPSException&)
9405 #else
9406 catch (AMPSException& e)
9407 #endif
9408 {
9409 current_.invalidate();
9410 _previousTopic.clear();
9411 _previousBookmark.clear();
9412 return false;
9413 }
9414 _previousTopic.clear();
9415 _previousBookmark.clear();
9416 }
9417 // Don't wait to wait more than 1s at a time
9418 long minWaitTime = (_timeout && _timeout < 1000) ? _timeout : 1000;
9419 Timer timer((double)_timeout);
9420 timer.start();
9421 while (_q.empty() && _state & Running)
9422 {
9423 // Using timeout so python can interrupt
9424 _lock.wait(minWaitTime);
9425 {
9426 Unlock<Mutex> unlck(_lock);
9427 amps_invoke_waiting_function();
9428 }
9429 if (_timeout)
9430 {
9431 // In case we woke up early, see how much longer to wait
9432 if (timer.checkAndGetRemaining(&minWaitTime))
9433 {
9434 // No time left
9435 break;
9436 }
9437 // Adjust next wait time
9438 minWaitTime = (minWaitTime < 1000) ? minWaitTime : 1000;
9439 }
9440 }
9441 if (current_.isValid() && _cache.size() < _cacheMax)
9442 {
9443 current_.reset();
9444 _cache.push_back(current_);
9445 }
9446 if (!_q.empty())
9447 {
9448 current_ = _q.front();
9449 if (_q.size() == _maxDepth)
9450 {
9451 _lock.signalAll();
9452 }
9453 _q.pop_front();
9454 if (_state == Conflate)
9455 {
9456 std::string sowKey = current_.getSowKey();
9457 if (sowKey.length())
9458 {
9459 _sowKeyMap.erase(sowKey);
9460 }
9461 }
9462 else if (_state == AcksOnly)
9463 {
9464 _requestedAcks &= ~(current_.getAckTypeEnum());
9465 }
9466 if ((_state == AcksOnly && _requestedAcks == 0) ||
9467 (_state == SOWOnly && current_.getCommand() == "group_end"))
9468 {
9469 _state = Closed;
9470 }
9471 else if (current_.isValid()
9472 && current_.getCommandEnum() == Message::Command::Publish
9473 && _client.isValid() && _client.getAutoAck()
9474 && !current_.getLeasePeriod().empty()
9475 && !current_.getBookmark().empty())
9476 {
9477 _previousTopic = current_.getTopic().deepCopy();
9478 _previousBookmark = current_.getBookmark().deepCopy();
9479 }
9480 return true;
9481 }
9482 if (_state == Disconnected)
9483 {
9484 throw DisconnectedException("Connection closed.");
9485 }
9486 current_.invalidate();
9487 if (_state == Closed)
9488 {
9489 return false;
9490 }
9491 return _timeout != 0;
9492 }
9493 void close(void)
9494 {
9495 if (_client.isValid())
9496 {
9497 if (_state == SOWOnly || _state == Subscribe) //not delete
9498 {
9499 if (!_commandId.empty())
9500 {
9501 _client.unsubscribe(_commandId);
9502 }
9503 if (!_subId.empty())
9504 {
9505 _client.unsubscribe(_subId);
9506 }
9507 if (!_queryId.empty())
9508 {
9509 _client.unsubscribe(_queryId);
9510 }
9511 }
9512 else
9513 {
9514 if (!_commandId.empty())
9515 {
9516 _client.removeMessageHandler(_commandId);
9517 }
9518 if (!_subId.empty())
9519 {
9520 _client.removeMessageHandler(_subId);
9521 }
9522 if (!_queryId.empty())
9523 {
9524 _client.removeMessageHandler(_queryId);
9525 }
9526 }
9527 }
9528 if (_state == SOWOnly || _state == Subscribe || _state == Unset)
9529 {
9530 _state = Closed;
9531 }
9532 }
9533 static void _messageHandler(const Message& message_, MessageStreamImpl* this_)
9534 {
9535 Lock<Mutex> lock(this_->_lock);
9536 if (this_->_state != Conflate)
9537 {
9538 AMPS_TESTING_SLOW_MESSAGE_STREAM
9539 if (this_->_q.size() >= this_->_maxDepth)
9540 {
9541 // We throw here so that heartbeats can be sent. The exception
9542 // will be handled internally only, and the same Message will
9543 // come back to try again. Make sure to signal.
9544 this_->_lock.signalAll();
9545 throw MessageStreamFullException("Stream is currently full.");
9546 }
9547 if (!this_->_cache.empty())
9548 {
9549 this_->_cache.front().deepCopy(message_);
9550 this_->_q.push_back(this_->_cache.front());
9551 this_->_cache.pop_front();
9552 }
9553 else
9554 {
9555 this_->_q.emplace_back(message_.deepCopy());
9556 }
9557 if (message_.getCommandEnum() == Message::Command::Publish &&
9558 this_->_client.isValid() && this_->_client.getAutoAck() &&
9559 !message_.getLeasePeriod().empty() &&
9560 !message_.getBookmark().empty())
9561 {
9562 message_.setIgnoreAutoAck();
9563 }
9564 }
9565 else
9566 {
9567 std::string sowKey = message_.getSowKey();
9568 if (sowKey.length())
9569 {
9570 SOWKeyMap::iterator it = this_->_sowKeyMap.find(sowKey);
9571 if (it != this_->_sowKeyMap.end())
9572 {
9573 it->second->deepCopy(message_);
9574 }
9575 else
9576 {
9577 if (this_->_q.size() >= this_->_maxDepth)
9578 {
9579 // We throw here so that heartbeats can be sent. The
9580 // exception will be handled internally only, and the
9581 // same Message will come back to try again. Make sure
9582 // to signal.
9583 this_->_lock.signalAll();
9584 throw MessageStreamFullException("Stream is currently full.");
9585 }
9586 if (!this_->_cache.empty())
9587 {
9588 this_->_cache.front().deepCopy(message_);
9589 this_->_q.push_back(this_->_cache.front());
9590 this_->_cache.pop_front();
9591 }
9592 else
9593 {
9594 this_->_q.emplace_back(message_.deepCopy());
9595 }
9596 this_->_sowKeyMap[sowKey] = &(this_->_q.back());
9597 }
9598 }
9599 else
9600 {
9601 if (this_->_q.size() >= this_->_maxDepth)
9602 {
9603 // We throw here so that heartbeats can be sent. The exception
9604 // will be handled internally only, and the same Message will
9605 // come back to try again. Make sure to signal.
9606 this_->_lock.signalAll();
9607 throw MessageStreamFullException("Stream is currently full.");
9608 }
9609 if (!this_->_cache.empty())
9610 {
9611 this_->_cache.front().deepCopy(message_);
9612 this_->_q.push_back(this_->_cache.front());
9613 this_->_cache.pop_front();
9614 }
9615 else
9616 {
9617 this_->_q.emplace_back(message_.deepCopy());
9618 }
9619 if (message_.getCommandEnum() == Message::Command::Publish &&
9620 this_->_client.isValid() && this_->_client.getAutoAck() &&
9621 !message_.getLeasePeriod().empty() &&
9622 !message_.getBookmark().empty())
9623 {
9624 message_.setIgnoreAutoAck();
9625 }
9626 }
9627 }
9628 this_->_lock.signalAll();
9629 }
9630 };
9631 inline MessageStream::MessageStream(const Client& client_)
9632 : _body(new MessageStreamImpl(client_))
9633 {
9634 }
9635 inline MessageStream::MessageStream(RefHandle<MessageStreamImpl> body_)
9636 : _body(body_)
9637 {
9638 }
9639 inline void MessageStream::iterator::advance(void)
9640 {
9641 _pStream = _pStream->_body->next(_current) ? _pStream : NULL;
9642 }
9643 inline MessageStream::operator MessageHandler(void)
9644 {
9645 return MessageHandler((void(*)(const Message&, void*))MessageStreamImpl::_messageHandler, &_body.get());
9646 }
9647 inline MessageStream MessageStream::fromExistingHandler(const MessageHandler& handler_)
9648 {
9649 MessageStream result;
9650 if (handler_._func == (MessageHandler::FunctionType)MessageStreamImpl::_messageHandler)
9651 {
9652 result._body = (MessageStreamImpl*)(handler_._userData);
9653 }
9654 return result;
9655 }
9656
9657 inline void MessageStream::setSOWOnly(const std::string& commandId_,
9658 const std::string& queryId_)
9659 {
9660 _body->setSOWOnly(commandId_, queryId_);
9661 }
9662 inline void MessageStream::setSubscription(const std::string& subId_,
9663 const std::string& commandId_,
9664 const std::string& queryId_)
9665 {
9666 _body->setSubscription(subId_, commandId_, queryId_);
9667 }
9668 inline void MessageStream::setStatsOnly(const std::string& commandId_,
9669 const std::string& queryId_)
9670 {
9671 _body->setStatsOnly(commandId_, queryId_);
9672 }
9673 inline void MessageStream::setAcksOnly(const std::string& commandId_,
9674 unsigned acks_)
9675 {
9676 _body->setAcksOnly(commandId_, acks_);
9677 }
9678 inline MessageStream MessageStream::timeout(unsigned timeout_)
9679 {
9680 _body->timeout(timeout_);
9681 return *this;
9682 }
9683 inline MessageStream MessageStream::conflate(void)
9684 {
9685 _body->conflate();
9686 return *this;
9687 }
9688 inline MessageStream MessageStream::maxDepth(unsigned maxDepth_)
9689 {
9690 _body->maxDepth(maxDepth_);
9691 return *this;
9692 }
9693 inline unsigned MessageStream::getMaxDepth(void) const
9694 {
9695 return _body->getMaxDepth();
9696 }
9697 inline unsigned MessageStream::getDepth(void) const
9698 {
9699 return _body->getDepth();
9700 }
9701
9702 inline MessageStream ClientImpl::getEmptyMessageStream(void)
9703 {
9704 return MessageStream(_pEmptyMessageStream.get()->_body);
9705 }
9706
9707 inline MessageStream Client::execute(Command& command_)
9708 {
9709 // If the command is sow and has a sub_id, OR
9710 // if the command has a replace option, return the existing
9711 // messagestream, don't create a new one.
9712 ClientImpl& body = _body.get();
9713 Message& message = command_.getMessage();
9714 Field subId = message.getSubscriptionId();
9715 unsigned ackTypes = message.getAckTypeEnum();
9716 bool useExistingHandler = !subId.empty() && ((!message.getOptions().empty() && message.getOptions().contains("replace", 7)) || message.getCommandEnum() == Message::Command::SOW);
9717 if (useExistingHandler)
9718 {
9719 // Try to find the existing message handler.
9720 if (!subId.empty())
9721 {
9722 MessageHandler existingHandler;
9723 if (body._routes.getRoute(subId, existingHandler))
9724 {
9725 // we found an existing handler. It might not be a message stream, but that's okay.
9726 body.executeAsync(command_, existingHandler, false);
9727 return MessageStream::fromExistingHandler(existingHandler);
9728 }
9729 }
9730 // fall through; we'll a new handler altogether.
9731 }
9732 // Make sure something will be returned to the stream or use the empty one
9733 // Check that: it's a command that doesn't normally return data, and there
9734 // are no acks requested for the cmd id
9735 Message::Command::Type command = message.getCommandEnum();
9736 if ((command & Message::Command::NoDataCommands)
9737 && (ackTypes == Message::AckType::Persisted
9738 || ackTypes == Message::AckType::None))
9739 {
9740 executeAsync(command_, MessageHandler());
9741 if (!body._pEmptyMessageStream)
9742 {
9743 body._pEmptyMessageStream.reset(new MessageStream((ClientImpl*)0));
9744 body._pEmptyMessageStream.get()->_body->close();
9745 }
9746 return body.getEmptyMessageStream();
9747 }
9748 MessageStream stream(*this);
9749 if (body.getDefaultMaxDepth())
9750 {
9751 stream.maxDepth(body.getDefaultMaxDepth());
9752 }
9753 MessageHandler handler = stream.operator MessageHandler();
9754 std::string commandID = body.executeAsync(command_, handler, false);
9755 if (command_.hasStatsAck())
9756 {
9757 stream.setStatsOnly(commandID, command_.getMessage().getQueryId());
9758 }
9759 else if (command_.isSow())
9760 {
9761 if (command_.getAckTypeEnum() & Message::AckType::Completed)
9762 {
9763 stream.setAcksOnly(commandID,
9764 ackTypes);
9765 }
9766 else
9767 {
9768 stream.setSOWOnly(commandID, command_.getMessage().getQueryId());
9769 }
9770 }
9771 else if (command_.isSubscribe())
9772 {
9773 stream.setSubscription(commandID,
9774 command_.getMessage().getCommandId(),
9775 command_.getMessage().getQueryId());
9776 }
9777 else
9778 {
9779 // Persisted acks for writes don't come back with command id
9780 if (command == Message::Command::Publish ||
9781 command == Message::Command::DeltaPublish ||
9782 command == Message::Command::SOWDelete)
9783 {
9784 stream.setAcksOnly(commandID,
9785 ackTypes & (unsigned)~Message::AckType::Persisted);
9786 }
9787 else
9788 {
9789 stream.setAcksOnly(commandID, ackTypes);
9790 }
9791 }
9792 return stream;
9793 }
9794
9795 // This is here because it uses api from Client.
9796 inline void Message::ack(const char* options_) const
9797 {
9798 ClientImpl* pClient = _body.get().clientImpl();
9799 Message::Field bookmark = getBookmark();
9800 if (pClient && bookmark.len() &&
9801 !pClient->getAutoAck())
9802 //(!pClient->getAutoAck() || getIgnoreAutoAck()))
9803 {
9804 pClient->ack(getTopic(), bookmark, options_);
9805 }
9806 }
9807 }// end namespace AMPS
9808 #endif
amps.h
Core type and function declarations for the AMPS C client.
amps_client_disconnect
AMPSDLL void amps_client_disconnect(amps_handle handle)
Disconnects from the AMPS server, if connected.
amps_message_get_field_value
AMPSDLL void amps_message_get_field_value(amps_handle message, FieldId field, const amps_char **value_ptr, size_t *length_ptr)
Retrieves the value of a header field in an AMPS message.
amps_client_destroy
AMPSDLL void amps_client_destroy(amps_handle handle)
Disconnects and destroys an AMPS client object.
amps_client_connect
AMPSDLL amps_result amps_client_connect(amps_handle handle, const amps_char *uri)
Connects to the AMPS server specified in uri.
amps_client_send
AMPSDLL amps_result amps_client_send(amps_handle client, amps_handle message)
Sends a message to the AMPS server.
amps_message_get_field_uint64
AMPSDLL amps_uint64_t amps_message_get_field_uint64(amps_handle message, FieldId field)
Gets the unsigned 64-bit int value of a header field in an AMPS message.
amps_client_set_name
AMPSDLL amps_result amps_client_set_name(amps_handle handle, const amps_char *clientName)
Sets the name on an amps client object.
amps_result
amps_result
Return values from amps_xxx functions.
Definition: amps.h:217
AMPS_E_RETRY
@ AMPS_E_RETRY
The operation has not succeeded, but ought to be retried.
Definition: amps.h:245
AMPS_E_OK
@ AMPS_E_OK
Success.
Definition: amps.h:221
AMPS_E_DISCONNECTED
@ AMPS_E_DISCONNECTED
The client and server are disconnected.
Definition: amps.h:249
amps_client_attempt_reconnect
AMPSDLL amps_result amps_client_attempt_reconnect(amps_handle client, unsigned version)
Manually invokes the user-supplied disconnect handler for this client.
amps_client_set_read_timeout
AMPSDLL amps_result amps_client_set_read_timeout(amps_handle client, int readTimeout)
Sets a read timeout (seconds), in which if no message is received, the connection is presumed dead.
amps_thread_created_callback
amps_result(* amps_thread_created_callback)(AMPS_THREAD_T, void *)
Prototype for a user-supplied callback function to allow thread attributes to be set when a new threa...
Definition: amps.h:696
amps_client_set_transport_filter_function
AMPSDLL amps_result amps_client_set_transport_filter_function(amps_handle client, amps_transport_filter_function filter, void *userData)
Sets a user-supplied callback function for filtering data before it is sent and after it is received.
amps_client_set_thread_created_callback
AMPSDLL amps_result amps_client_set_thread_created_callback(amps_handle client, amps_thread_created_callback callback, void *userData)
Sets a user-supplied callback function to allow thread attributes to set when a new thread is created...
amps_message_set_field_value
AMPSDLL void amps_message_set_field_value(amps_handle message, FieldId field, const amps_char *value, size_t length)
Sets the value of a header field in an AMPS message.
amps_client_set_predisconnect_handler
AMPSDLL void amps_client_set_predisconnect_handler(amps_handle client, amps_predisconnect_handler predisconnectHandler, void *userData)
Sets the predisconnect handler function to be called when a disconnect occurs.
amps_client_set_message_handler
AMPSDLL void amps_client_set_message_handler(amps_handle client, amps_handler messageHandler, void *userData)
Sets the message handler function for this client.
amps_client_send_batch
AMPSDLL amps_result amps_client_send_batch(amps_handle client, amps_handle message, unsigned *version_out, int addToBatch)
Adds a message to the send cache, possibly sending the cache.
amps_client_get_socket
AMPSDLL AMPS_SOCKET amps_client_get_socket(amps_handle client)
Returns the socket from the underlying transport in client, or NULL if no transport is associated wit...
amps_handle
void * amps_handle
Opaque handle type used to refer to objects in the AMPS api.
Definition: amps.h:211
amps_transport_filter_function
void(* amps_transport_filter_function)(const unsigned char *, size_t, short, void *)
Prototype for a user-supplied callback function for filtering data before it is sent and after it is ...
Definition: amps.h:670
amps_client_set_idle_time
AMPSDLL amps_result amps_client_set_idle_time(amps_handle client, int idleTime)
Sets an idle-time (milliseconds).
amps_client_set_batch_send
AMPSDLL void amps_client_set_batch_send(amps_handle client_, amps_uint64_t batchSizeBytes_, amps_uint64_t batchTimeout_)
Sets a byte size batchSizeBytes and timeout for using batch sends of publish and delta_publish messag...
amps_client_create
AMPSDLL amps_handle amps_client_create(const amps_char *clientName)
Functions for creation of an AMPS client.
amps_client_set_disconnect_handler
AMPSDLL void amps_client_set_disconnect_handler(amps_handle client, amps_handler disconnectHandler, void *userData)
Sets the disconnect handler function to be called when a disconnect occurs.
amps_client_set_http_preflight_callback
AMPSDLL amps_result amps_client_set_http_preflight_callback(amps_handle client, amps_http_preflight_callback callback, void *userData)
Sets a user-supplied callback function for when a connection is established and the provided uri incl...
AMPS::DangerousFlushPublishStoreResizeHandler
bool DangerousFlushPublishStoreResizeHandler(Store store_, size_t, void *data_)
PublishStoreResizeHandler that will block up to the timeout specified in user data milliseconds tryin...
Definition: ampsplusplus.hpp:1411
AMPS::PublishStoreResizeHandler
bool(* PublishStoreResizeHandler)(Store store_, size_t size_, void *userData_)
Function type for PublishStore resize events The store_ param is store which is resizing.
Definition: ampsplusplus.hpp:1091
AMPS::AMPSException
Base class for all exceptions in AMPS.
Definition: AMPSException.hpp:41
AMPS::AMPSException::throwFor
static void throwFor(const T &context_, amps_result result_)
Constructs and throws the appropriate exception corresponding to a given result.
AMPS::Authenticator
The interface for handling authentication with the AMPS server.
Definition: ampsplusplus.hpp:1007
AMPS::Authenticator::authenticate
virtual std::string authenticate(const std::string &userName_, const std::string &password_)=0
Called by Client just before the logon command is sent.
AMPS::Authenticator::completed
virtual void completed(const std::string &userName_, const std::string &password_, const std::string &reason_)=0
Called by Client once a logon completes successfully.
AMPS::Authenticator::retry
virtual std::string retry(const std::string &userName_, const std::string &password_)=0
Called by Client when a logon ack is received with a status of retry.
AMPS::BookmarkStore
Interface for BookmarkStoreImpl classes.
Definition: BookmarkStore.hpp:229
AMPS::Client
Client represents a connection to an AMPS server, but does not provide failover or reconnection behav...
Definition: ampsplusplus.hpp:5507
AMPS::Client::publish
amps_uint64_t publish(const std::string &topic_, const std::string &data_, const std::string &correlationId_, const std::string &sowKey_, unsigned long expiration_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store (if a...
Definition: ampsplusplus.hpp:6291
AMPS::Client::sow
MessageStream sow(const char *topic_, const std::string &filter_="", const std::string &orderBy_="", const std::string &bookmark_="", int batchSize_=DEFAULT_BATCH_SIZE, int topN_=DEFAULT_TOP_N, const std::string &options_="", long timeout_=DEFAULT_COMMAND_TIMEOUT)
Query the SOW cache of a topic.
Definition: ampsplusplus.hpp:7043
AMPS::Client::publish
amps_uint64_t publish(const char *topic_, size_t topicLength_, const char *data_, size_t dataLength_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store if on...
Definition: ampsplusplus.hpp:6034
AMPS::Client::setExceptionListener
void setExceptionListener(const std::shared_ptr< const ExceptionListener > &pListener_)
Sets the exception listener for exceptions that are not thrown back to the user (for example,...
Definition: ampsplusplus.hpp:7781
AMPS::Client::logon
std::string logon(const char *options_, int timeout_=0)
Logon to the server, providing the client name, credentials (if available) client information (such a...
Definition: ampsplusplus.hpp:6651
AMPS::Client::flushAcks
void flushAcks(void)
Sends any queued message queue ack messages to the server immediately.
Definition: ampsplusplus.hpp:8193
AMPS::Client::startTimer
void startTimer()
Definition: ampsplusplus.hpp:7612
AMPS::Client::subscribe
MessageStream subscribe(const std::string &topic_, long timeout_=0, const std::string &filter_="", const std::string &options_="", const std::string &subId_="")
Subscribe to a topic.
Definition: ampsplusplus.hpp:6721
AMPS::Client::setBookmarkStore
void setBookmarkStore(const BookmarkStore &bookmarkStore_)
Set the bookmark store to be used by the client.
Definition: ampsplusplus.hpp:5881
AMPS::Client::getURI
const std::string & getURI() const
Returns the last URI this client is connected to.
Definition: ampsplusplus.hpp:5702
AMPS::Client::removeConnectionStateListener
void removeConnectionStateListener(ConnectionStateListener *listener)
Attempts to remove listener from self's set of ConnectionStateListeners.
Definition: ampsplusplus.hpp:8012
AMPS::Client::publish
amps_uint64_t publish(const std::string &topic_, const std::string &data_, unsigned long expiration_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store (if a...
Definition: ampsplusplus.hpp:6058
AMPS::Client::BOOKMARK_EPOCH
static const char * BOOKMARK_EPOCH()
Convenience method for returning the special value to start a subscription at the beginning of the tr...
Definition: ampsplusplus.hpp:7950
AMPS::Client::publish
amps_uint64_t publish(const char *topic_, size_t topicLength_, const char *data_, size_t dataLength_, const char *correlationId_, size_t correlationIdLength_, const char *sowKey_, size_t sowKeyLength_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store if on...
Definition: ampsplusplus.hpp:6261
AMPS::Client::deltaSubscribe
std::string deltaSubscribe(const MessageHandler &messageHandler_, const std::string &topic_, long timeout_, const std::string &filter_="", const std::string &options_="", const std::string &subId_="")
Delta Subscribe to a topic.
Definition: ampsplusplus.hpp:6782
AMPS::Client::getSubscriptionManager
SubscriptionManager * getSubscriptionManager()
Get the subscription manager being used by the client.
Definition: ampsplusplus.hpp:5897
AMPS::Client::setPublishBatching
void setPublishBatching(size_t batchSize_, amps_uint64_t batchTimeoutMillis_)
Sets the max bytes to cache and max timeout in millis for caching delta_publish and publish commands.
Definition: ampsplusplus.hpp:8335
AMPS::Client::bookmarkSubscribe
std::string bookmarkSubscribe(const MessageHandler &messageHandler_, const std::string &topic_, long timeout_, const std::string &bookmark_, const std::string &filter_="", const std::string &options_="", const std::string &subId_="")
Subscribe to a topic using a bookmark.
Definition: ampsplusplus.hpp:6860
AMPS::Client::addHttpPreflightHeader
void addHttpPreflightHeader(const std::string &key_, const std::string &value_)
Adds a given key/value pair as an HTTP header line as "key: value" to the end of the headers that wil...
Definition: ampsplusplus.hpp:5627
AMPS::Client::unsubscribe
void unsubscribe()
Unsubscribe from all topics.
Definition: ampsplusplus.hpp:6949
AMPS::Client::setHeartbeat
void setHeartbeat(unsigned heartbeatTime_)
Requests heartbeating with the AMPS server.
Definition: ampsplusplus.hpp:7854
AMPS::Client::setLastChanceMessageHandler
void setLastChanceMessageHandler(const AMPS::MessageHandler &messageHandler)
Sets the message handler called when no other handler matches.
Definition: ampsplusplus.hpp:7870
AMPS::Client::BOOKMARK_MOST_RECENT
static const char * BOOKMARK_MOST_RECENT()
Convenience method for returning the special value to start a subscription at a recovery point based ...
Definition: ampsplusplus.hpp:7971
AMPS::Client::getHandle
amps_handle getHandle()
Returns the underlying amps_handle for this client, to be used with amps_client_* functions from the ...
Definition: ampsplusplus.hpp:7768
AMPS::Client::publish
amps_uint64_t publish(const char *topic_, size_t topicLength_, const char *data_, size_t dataLength_, const char *correlationId_, size_t correlationIdLength_, const char *sowKey_, size_t sowKeyLength_, unsigned long expiration_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store if on...
Definition: ampsplusplus.hpp:6327
AMPS::Client::setFailedWriteHandler
void setFailedWriteHandler(FailedWriteHandler *handler_)
Set the handler that is invoked to report when a publish fails, for example if the publisher is not e...
Definition: ampsplusplus.hpp:5978
AMPS::Client::setName
void setName(const std::string &name)
Sets the name of this client, assuming no name was provided previously.
Definition: ampsplusplus.hpp:5561
AMPS::Client::deltaPublish
amps_uint64_t deltaPublish(const std::string &topic_, const std::string &data_)
Publish the changed fields of a message to an AMPS topic.
Definition: ampsplusplus.hpp:6537
AMPS::Client::getFailedWriteHandler
FailedWriteHandler * getFailedWriteHandler()
Get the handler that is invoked to report on failed writes.
Definition: ampsplusplus.hpp:5986
AMPS::Client::setHttpPreflightHeaders
void setHttpPreflightHeaders(const T &headers_)
Sets the given HTTP header lines to be sent for the HTTP GET Upgrade request.
Definition: ampsplusplus.hpp:5642
AMPS::Client::setDefaultMaxDepth
void setDefaultMaxDepth(unsigned maxDepth_)
Sets a default max depth on all subsequently created MessageStream objects.
Definition: ampsplusplus.hpp:8289
AMPS::Client::publish
amps_uint64_t publish(const std::string &topic_, const std::string &data_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store if on...
Definition: ampsplusplus.hpp:6009
AMPS::Client::sowAndDeltaSubscribe
MessageStream sowAndDeltaSubscribe(const char *topic_, const std::string &filter_="", const std::string &orderBy_="", int batchSize_=DEFAULT_BATCH_SIZE, int topN_=DEFAULT_TOP_N, const std::string &options_="", long timeout_=DEFAULT_COMMAND_TIMEOUT)
Query the SOW cache of a topic and initiates a new delta subscription on it.
Definition: ampsplusplus.hpp:7397
AMPS::Client::setAckBatchSize
void setAckBatchSize(const unsigned ackBatchSize_)
Sets the queue ack batch size setting.
Definition: ampsplusplus.hpp:8230
AMPS::Client::publish
amps_uint64_t publish(Message &message_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store if on...
Definition: ampsplusplus.hpp:6356
AMPS::Client::disconnect
void disconnect()
Disconnect from an AMPS server.
Definition: ampsplusplus.hpp:5733
AMPS::Client::publish
amps_uint64_t publish(const std::string &topic_, const std::string &data_, const std::string &correlationId_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store if on...
Definition: ampsplusplus.hpp:6111
AMPS::Client::NOW
static const char * NOW()
Convenience method for returning the special value to start a subscription at the end of the transact...
Definition: ampsplusplus.hpp:7940
AMPS::Client::clearHttpPreflightHeaders
void clearHttpPreflightHeaders()
Clears all previously set HTTP header lines.
Definition: ampsplusplus.hpp:5633
AMPS::Client::deltaPublish
amps_uint64_t deltaPublish(const char *topic_, size_t topicLength_, const char *data_, size_t dataLength_)
Publish the changed fields of a message to an AMPS topic.
Definition: ampsplusplus.hpp:6560
AMPS::Client::convertVersionToNumber
static size_t convertVersionToNumber(const char *data_, size_t len_)
Converts a string version, such as "3.8.1.5" into the same numeric form used internally and returned ...
Definition: ampsplusplus.hpp:5695
AMPS::Client::Client
Client(const std::string &clientName="")
Constructs a new client with a given client name.
Definition: ampsplusplus.hpp:5523
AMPS::Client::publishKeyed
amps_uint64_t publishKeyed(const std::string &topic_, const std::string &data_, const std::string &sowKey_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store if on...
Definition: ampsplusplus.hpp:6379
AMPS::Client::getExceptionListener
const ExceptionListener & getExceptionListener(void) const
Returns the exception listener set on this Client.
Definition: ampsplusplus.hpp:7804
AMPS::Client::publish
amps_uint64_t publish(const char *topic_, size_t topicLength_, const char *data_, size_t dataLength_, const char *correlationId_, size_t correlationIdLength_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store if on...
Definition: ampsplusplus.hpp:6140
AMPS::Client::unsubscribe
void unsubscribe(const std::string &commandId)
Unsubscribe from a topic.
Definition: ampsplusplus.hpp:6937
AMPS::Client::setExceptionListener
void setExceptionListener(const ExceptionListener &listener_)
Definition: ampsplusplus.hpp:7797
AMPS::Client::getDisconnectHandler
DisconnectHandler getDisconnectHandler(void) const
Definition: ampsplusplus.hpp:5859
AMPS::Client::getAckTimeout
int getAckTimeout(void) const
Returns the current value of the message queue ack timeout setting – that is, the amount of time afte...
Definition: ampsplusplus.hpp:8241
AMPS::Client::bookmarkSubscribe
MessageStream bookmarkSubscribe(const std::string &topic_, long timeout_, const std::string &bookmark_, const std::string &filter_="", const std::string &options_="", const std::string &subId_="")
Subscribe to a topic using a bookmark.
Definition: ampsplusplus.hpp:6888
AMPS::Client::getRetryOnDisconnect
bool getRetryOnDisconnect(void) const
Returns true if automatic retry of a command to AMPS after a reconnect is enabled.
Definition: ampsplusplus.hpp:8280
AMPS::Client::getBookmarkStore
BookmarkStore getBookmarkStore()
Get the bookmark store being used by the client.
Definition: ampsplusplus.hpp:5889
AMPS::Client::subscribe
MessageStream subscribe(const char *topic_, long timeout_=0, const std::string &filter_="", const std::string &options_="", const std::string &subId_="")
Subscribe to a topic.
Definition: ampsplusplus.hpp:6753
AMPS::Client::getLogonCorrelationData
const std::string & getLogonCorrelationData() const
Returns the currently set logon correlation data for the client.
Definition: ampsplusplus.hpp:5610
AMPS::Client::logon
std::string logon(int timeout_=0, Authenticator &authenticator_=DefaultAuthenticator::instance(), const char *options_=NULL)
Logon to the server, providing the client name, credentials (if available), and client information (s...
Definition: ampsplusplus.hpp:6632
AMPS::Client::publish
amps_uint64_t publish(const char *topic_, size_t topicLength_, const char *data_, size_t dataLength_, const char *correlationId_, size_t correlationIdLength_, unsigned long expiration_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store if on...
Definition: ampsplusplus.hpp:6199
AMPS::Client::getNameHashValue
const amps_uint64_t getNameHashValue() const
Returns the numeric name hash of this client as generated by the server and returned when the client ...
Definition: ampsplusplus.hpp:5584
AMPS::Client::setGlobalCommandTypeMessageHandler
void setGlobalCommandTypeMessageHandler(const Message::Command::Type command_, const MessageHandler &handler_)
Sets a handler for all messages of a particular type: currently supported types are heartbeat message...
Definition: ampsplusplus.hpp:7921
AMPS::Client::getName
const std::string & getName() const
Returns the name of this client passed in the constructor.
Definition: ampsplusplus.hpp:5568
AMPS::Client::getAutoAck
bool getAutoAck(void) const
Returns the value of the queue auto-ack setting.
Definition: ampsplusplus.hpp:8202
AMPS::Client::getDefaultMaxDepth
unsigned getDefaultMaxDepth(void) const
Returns the default max depth for returned MessageStream objects.
Definition: ampsplusplus.hpp:8298
AMPS::Client::setSubscriptionManager
void setSubscriptionManager(SubscriptionManager *subscriptionManager_)
Set the subscription manager to be used by the client.
Definition: ampsplusplus.hpp:5909
AMPS::Client::publishKeyed
amps_uint64_t publishKeyed(const char *topic_, size_t topicLength_, const char *data_, size_t dataLength_, const char *sowKey_, size_t sowKeyLength_, unsigned long expiration_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store if on...
Definition: ampsplusplus.hpp:6467
AMPS::Client::publishKeyed
amps_uint64_t publishKeyed(const std::string &topic_, const std::string &data_, const std::string &sowKey_, unsigned long expiration_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store (if a...
Definition: ampsplusplus.hpp:6435
AMPS::Client::ack
void ack(Field &topic_, Field &bookmark_, const char *options_=NULL)
Acknowledge a message queue message by supplying a topic and bookmark: this adds the ack to the curre...
Definition: ampsplusplus.hpp:8145
AMPS::Client::clearConnectionStateListeners
void clearConnectionStateListeners()
Clear all listeners from self's set of ConnectionStateListeners.
Definition: ampsplusplus.hpp:8019
AMPS::Client::BOOKMARK_NOW
static const char * BOOKMARK_NOW()
Convenience method for returning the special value to start a subscription at the end of the transact...
Definition: ampsplusplus.hpp:7931
AMPS::Client::bookmarkSubscribe
MessageStream bookmarkSubscribe(const char *topic_, long timeout_, const std::string &bookmark_, const std::string &filter_="", const std::string &options_="", const std::string &subId_="")
Subscribe to a topic using a bookmark.
Definition: ampsplusplus.hpp:6909
AMPS::Client::executeAsyncNoResubscribe
std::string executeAsyncNoResubscribe(Command &command_, MessageHandler handler_)
Execute the provided command and, once AMPS acknowledges the command, process messages in response to...
Definition: ampsplusplus.hpp:8083
AMPS::Client::getServerVersion
size_t getServerVersion() const
Returns the server version retrieved during logon.
Definition: ampsplusplus.hpp:5655
AMPS::Client::EPOCH
static const char * EPOCH()
Convenience method for returning the special value to start a subscription at the beginning of the tr...
Definition: ampsplusplus.hpp:7960
AMPS::Client::send
void send(const Message &message)
Sends a Message to the connected AMPS server, performing only minimal validation and bypassing client...
Definition: ampsplusplus.hpp:5751
AMPS::Client::publishFlush
void publishFlush(long timeout_=0, unsigned ackType_=Message::AckType::Processed)
Ensure that AMPS messages are sent and have been processed by the AMPS server.
Definition: ampsplusplus.hpp:6516
AMPS::Client::deltaPublish
amps_uint64_t deltaPublish(const char *topic_, size_t topicLength_, const char *data_, size_t dataLength_, unsigned long expiration_)
Publish the changed fields of a message to an AMPS topic.
Definition: ampsplusplus.hpp:6609
AMPS::Client::ack
void ack(const std::string &topic_, const std::string &bookmark_, const char *options_=NULL)
Acknowledge a message queue message by supplying a topic and bookmark string: this adds the ack to th...
Definition: ampsplusplus.hpp:8169
AMPS::Client::deltaSubscribe
MessageStream deltaSubscribe(const char *topic_, long timeout_, const std::string &filter_="", const std::string &options_="", const std::string &subId_="")
Delta Subscribe to a topic.
Definition: ampsplusplus.hpp:6818
AMPS::Client::setUnhandledMessageHandler
void setUnhandledMessageHandler(const AMPS::MessageHandler &messageHandler)
Definition: ampsplusplus.hpp:7863
AMPS::Client::addMessageHandler
void addMessageHandler(const Field &commandId_, const AMPS::MessageHandler &messageHandler_, unsigned requestedAcks_, bool isSubscribe_)
Adds a MessageHandler to be invoked for Messages with the given CommandId as their command id,...
Definition: ampsplusplus.hpp:5764
AMPS::Client::publish
amps_uint64_t publish(const std::string &topic_, const std::string &data_, const std::string &correlationId_, unsigned long expiration_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store (if a...
Definition: ampsplusplus.hpp:6167
AMPS::Client::deltaPublish
amps_uint64_t deltaPublish(const std::string &topic_, const std::string &data_, unsigned long expiration_)
Publish the changed fields of a message to an AMPS topic.
Definition: ampsplusplus.hpp:6583
AMPS::Client::addMessageHandler
void addMessageHandler(const Field &commandId_, const AMPS::MessageHandler &messageHandler_, unsigned requestedAcks_, Message::Command::Type commandType_)
Adds a MessageHandler to be invoked for Messages with the given CommandId as their command id,...
Definition: ampsplusplus.hpp:5781
AMPS::Client::executeAsync
std::string executeAsync(Command &command_, MessageHandler handler_)
Execute the provided command and, once AMPS acknowledges the command, process messages in response to...
Definition: ampsplusplus.hpp:8049
AMPS::Client::getAckBatchSize
unsigned getAckBatchSize(void) const
Returns the value of the queue ack batch size setting.
Definition: ampsplusplus.hpp:8220
AMPS::Client::setAutoAck
void setAutoAck(bool isAutoAckEnabled_)
Sets the queue auto-ack setting on this client.
Definition: ampsplusplus.hpp:8212
AMPS::Client::addHttpPreflightHeader
void addHttpPreflightHeader(const std::string &header_)
Adds a given HTTP header line to the end of the headers that will be sent for the HTTP GET Upgrade re...
Definition: ampsplusplus.hpp:5618
AMPS::Client::execute
MessageStream execute(Command &command_)
Execute the provided command and return messages received in response in a MessageStream.
Definition: ampsplusplus.hpp:9707
AMPS::Client::connect
void connect(const std::string &uri)
Connect to an AMPS server.
Definition: ampsplusplus.hpp:5726
AMPS::Client::getNameHash
const std::string & getNameHash() const
Returns the name hash string of this client as generated by the server and returned when the client l...
Definition: ampsplusplus.hpp:5576
AMPS::Client::MOST_RECENT
static const char * MOST_RECENT()
Convenience method for returning the special value to start a subscription at a recovery point based ...
Definition: ampsplusplus.hpp:7982
AMPS::Client::publishKeyed
amps_uint64_t publishKeyed(const char *topic_, size_t topicLength_, const char *data_, size_t dataLength_, const char *sowKey_, size_t sowKeyLength_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store if on...
Definition: ampsplusplus.hpp:6408
AMPS::Client::removeMessageHandler
bool removeMessageHandler(const Field &commandId_)
Removes a MessageHandler for a given ComandId from self.
Definition: ampsplusplus.hpp:5792
AMPS::Client::setRetryOnDisconnect
void setRetryOnDisconnect(bool isRetryOnDisconnect_)
Enables or disables automatic retry of a command to AMPS after a reconnect.
Definition: ampsplusplus.hpp:8271
AMPS::Client::setGlobalCommandTypeMessageHandler
void setGlobalCommandTypeMessageHandler(const std::string &command_, const MessageHandler &handler_)
Sets a handler for all messages of a particular type, or for messages that would be delivered to a pa...
Definition: ampsplusplus.hpp:7896
AMPS::Client::setTransportFilterFunction
void setTransportFilterFunction(amps_transport_filter_function filter_, void *userData_)
Sets a filter function on the transport that is called with all raw data sent or received.
Definition: ampsplusplus.hpp:8309
AMPS::Client::getConnectionName
const std::string & getConnectionName() const
Returns the connection name string of this client as generated by the server and returned when the cl...
Definition: ampsplusplus.hpp:5592
AMPS::Client::getServerVersionInfo
VersionInfo getServerVersionInfo() const
Returns the server version retrieved during logon.
Definition: ampsplusplus.hpp:5666
AMPS::Client::setPublishStore
void setPublishStore(const Store &publishStore_)
Set the publish store to be used by the client.
Definition: ampsplusplus.hpp:5933
AMPS::Client::setHeartbeat
void setHeartbeat(unsigned heartbeatTime_, unsigned readTimeout_)
Requests heartbeating with the AMPS server.
Definition: ampsplusplus.hpp:7830
AMPS::Client::deltaSubscribe
MessageStream deltaSubscribe(const std::string &topic_, long timeout_, const std::string &filter_="", const std::string &options_="", const std::string &subId_="")
Delta Subscribe to a topic.
Definition: ampsplusplus.hpp:6800
AMPS::Client::setLogonCorrelationData
void setLogonCorrelationData(const std::string &logonCorrelationData_)
Sets the logon correlation data for the client.
Definition: ampsplusplus.hpp:5603
AMPS::Client::addConnectionStateListener
void addConnectionStateListener(ConnectionStateListener *listener)
Adds a ConnectionStateListener to self's set of listeners.
Definition: ampsplusplus.hpp:8004
AMPS::Client::setThreadCreatedCallback
void setThreadCreatedCallback(amps_thread_created_callback callback_, void *userData_)
Sets a callback function on the transport that is called when a new thread is created to receive data...
Definition: ampsplusplus.hpp:8323
AMPS::Client::subscribe
std::string subscribe(const MessageHandler &messageHandler_, const std::string &topic_, long timeout_=0, const std::string &filter_="", const std::string &options_="", const std::string &subId_="")
Subscribe to a topic.
Definition: ampsplusplus.hpp:6695
AMPS::Client::setDisconnectHandler
virtual void setDisconnectHandler(const DisconnectHandler &disconnectHandler)
Definition: ampsplusplus.hpp:5845
AMPS::Client::setAckTimeout
void setAckTimeout(const int ackTimeout_)
Sets the message queue ack timeout value.
Definition: ampsplusplus.hpp:8253
AMPS::Client::getDuplicateMessageHandler
MessageHandler getDuplicateMessageHandler(void)
Returns the callback function that is invoked when a duplicate message is detected.
Definition: ampsplusplus.hpp:5964
AMPS::Client::getConnectionInfo
virtual ConnectionInfo getConnectionInfo() const
Get the connection information for the current connection.
Definition: ampsplusplus.hpp:5868
AMPS::Client::send
std::string send(const MessageHandler &messageHandler_, Message &message_, int timeout_=0, AMPS_ATOMIC_BASE_TYPE subscribeGeneration_=0)
Sends a Message to the connected AMPS server, performing only minimal validation and bypassing client...
Definition: ampsplusplus.hpp:5821
AMPS::Client::setDuplicateMessageHandler
void setDuplicateMessageHandler(const MessageHandler &duplicateMessageHandler_)
Sets a callback function that is invoked when a duplicate message is detected.
Definition: ampsplusplus.hpp:5949
AMPS::Client::sowAndSubscribe
MessageStream sowAndSubscribe(const char *topic_, const std::string &filter_="", const std::string &orderBy_="", const std::string &bookmark_="", int batchSize_=DEFAULT_BATCH_SIZE, int topN_=DEFAULT_TOP_N, const std::string &options_="", long timeout_=DEFAULT_COMMAND_TIMEOUT)
Query the SOW cache of a topic and initiates a new subscription on it.
Definition: ampsplusplus.hpp:7296
AMPS::Client::BOOKMARK_RECENT
static const char * BOOKMARK_RECENT()
Convenience method for returning the special value to start a subscription at a recovery point based ...
Definition: ampsplusplus.hpp:7993
AMPS::Client::stopTimer
std::string stopTimer(const MessageHandler &messageHandler)
Definition: ampsplusplus.hpp:7623
AMPS::Client::ack
void ack(Message &message_, const char *options_=NULL)
Acknowledge a message queue message by supplying the message directly: this adds the ack to the curre...
Definition: ampsplusplus.hpp:8157
AMPS::Client::convertVersionToNumber
static size_t convertVersionToNumber(const std::string &version_)
Converts a string version, such as "3.8.1.5" into the same numeric form used internally and returned ...
Definition: ampsplusplus.hpp:5680
AMPS::Client::publish
amps_uint64_t publish(const char *topic_, size_t topicLength_, const char *data_, size_t dataLength_, unsigned long expiration_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store if on...
Definition: ampsplusplus.hpp:6085
AMPS::Client::getPublishStore
Store getPublishStore()
Get the publish store used by the client.
Definition: ampsplusplus.hpp:5941
AMPS::Client::publish
amps_uint64_t publish(const std::string &topic_, const std::string &data_, const std::string &correlationId_, const std::string &sowKey_)
Publish a message to an AMPS topic, returning the sequence number assigned by the publish store if on...
Definition: ampsplusplus.hpp:6228
AMPS::Client::logon
std::string logon(const std::string &options_, int timeout_=0)
Logon to the server, providing the client name, credentials (if available) client information (such a...
Definition: ampsplusplus.hpp:6670
AMPS::Command
Command is an encapsulation of a single AMPS command sent by the client.
Definition: ampsplusplus.hpp:473
AMPS::Command::setData
Command & setData(const char *data_, size_t dataLen_)
Sets the data for this command.
Definition: ampsplusplus.hpp:837
AMPS::Command::setTopN
Command & setTopN(unsigned topN_)
Definition: ampsplusplus.hpp:857
AMPS::Command::setSequence
Command & setSequence(const char *seq_, size_t seqLen_)
Definition: ampsplusplus.hpp:810
AMPS::Command::setTopic
Command & setTopic(const std::string &topic_)
Definition: ampsplusplus.hpp:679
AMPS::Command::setAckType
Command & setAckType(unsigned ackType_)
Definition: ampsplusplus.hpp:931
AMPS::Command::setOrderBy
Command & setOrderBy(const std::string &orderBy_)
Definition: ampsplusplus.hpp:705
AMPS::Command::setOptions
Command & setOptions(const char *options_, size_t optionsLen_)
Sets the options string for this command: see Message.Options for a helper class for constructing the...
Definition: ampsplusplus.hpp:797
AMPS::Command::setSowKey
Command & setSowKey(const char *sowKey_, size_t sowKeyLen_)
Sets the SowKey field of the command, typically used for a publish command to a topic in the state of...
Definition: ampsplusplus.hpp:625
AMPS::Command::setAckType
Command & setAckType(const std::string &ackType_)
Definition: ampsplusplus.hpp:909
AMPS::Command::setTimeout
Command & setTimeout(unsigned timeout_)
Sets the client-side timeout for this command.
Definition: ampsplusplus.hpp:851
AMPS::Command::setBookmark
Command & setBookmark(const char *bookmark_, size_t bookmarkLen_)
Set the bookmark to be used this command.
Definition: ampsplusplus.hpp:759
AMPS::Command::setQueryId
Command & setQueryId(const char *queryId_, size_t queryIdLen_)
Definition: ampsplusplus.hpp:738
AMPS::Command::getAckType
std::string getAckType() const
Definition: ampsplusplus.hpp:953
AMPS::Command::reset
Command & reset(Message::Command::Type command_)
Resets the fields of self, and sets the command to command_.
Definition: ampsplusplus.hpp:600
AMPS::Command::setBatchSize
Command & setBatchSize(unsigned batchSize_)
Sets the batch size for this command, which controls how many records are sent together in the result...
Definition: ampsplusplus.hpp:873
AMPS::Command::setSubId
Command & setSubId(const char *subId_, size_t subIdLen_)
Definition: ampsplusplus.hpp:725
AMPS::Command::setSequence
Command & setSequence(const amps_uint64_t seq_)
Definition: ampsplusplus.hpp:816
AMPS::Command::setOptions
Command & setOptions(const std::string &options_)
Sets the options string for this command: see Message.Options for a helper class for constructing the...
Definition: ampsplusplus.hpp:789
AMPS::Command::setSowKeys
Command & setSowKeys(const char *sowKeys_, size_t sowKeysLen_)
Sets the SowKeys for the command.
Definition: ampsplusplus.hpp:660
AMPS::Command::setSubId
Command & setSubId(const std::string &subId_)
Definition: ampsplusplus.hpp:718
AMPS::Command::setCommandId
Command & setCommandId(const std::string &cmdId_)
Definition: ampsplusplus.hpp:666
AMPS::Command::setSequence
Command & setSequence(const std::string &seq_)
Definition: ampsplusplus.hpp:803
AMPS::Command::setCommandId
Command & setCommandId(const char *cmdId_, size_t cmdIdLen_)
Definition: ampsplusplus.hpp:673
AMPS::Command::Command
Command(Message::Command::Type command_)
Creates an object to represent the given AMPS command, such as "sow" or "subscribe".
Definition: ampsplusplus.hpp:575
AMPS::Command::setOrderBy
Command & setOrderBy(const char *orderBy_, size_t orderByLen_)
Definition: ampsplusplus.hpp:712
AMPS::Command::setData
Command & setData(const std::string &data_)
Sets the data for this command from an existing string.
Definition: ampsplusplus.hpp:829
AMPS::Command::setQueryId
Command & setQueryId(const std::string &queryId_)
Definition: ampsplusplus.hpp:731
AMPS::Command::Command
Command(const std::string &command_)
Creates an object to represent the given AMPS command, such as "sow" or "subscribe".
Definition: ampsplusplus.hpp:560
AMPS::Command::setTopic
Command & setTopic(const char *topic_, size_t topicLen_)
Definition: ampsplusplus.hpp:686
AMPS::Command::setFilter
Command & setFilter(const std::string &filter_)
Definition: ampsplusplus.hpp:692
AMPS::Command::setCorrelationId
Command & setCorrelationId(const std::string &correlationId_)
Set the correlation ID for this command.
Definition: ampsplusplus.hpp:770
AMPS::Command::setFilter
Command & setFilter(const char *filter_, size_t filterLen_)
Definition: ampsplusplus.hpp:699
AMPS::Command::Command
Command(const char *command_, size_t commandLen_)
Creates an object to represent the given AMPS command, such as "sow" or "subscribe".
Definition: ampsplusplus.hpp:568
AMPS::Command::setCorrelationId
Command & setCorrelationId(const char *correlationId_, size_t correlationIdLen_)
Set the correlation ID for this command.
Definition: ampsplusplus.hpp:782
AMPS::Command::getAckTypeEnum
unsigned getAckTypeEnum() const
Definition: ampsplusplus.hpp:958
AMPS::Command::setSowKey
Command & setSowKey(const std::string &sowKey_)
Sets the SowKey field of the command, typically used for a publish command to a topic in the state of...
Definition: ampsplusplus.hpp:612
AMPS::Command::setSowKeys
Command & setSowKeys(const std::string &sowKeys_)
Sets the SowKeys for the command.
Definition: ampsplusplus.hpp:642
AMPS::Command::addAckType
Command & addAckType(const std::string &ackType_)
Definition: ampsplusplus.hpp:895
AMPS::Command::reset
Command & reset(const char *command_, size_t commandLen_)
Resets the fields of self, and sets the command to command_.
Definition: ampsplusplus.hpp:592
AMPS::Command::setExpiration
Command & setExpiration(unsigned expiration_)
Set the expiration time for a publish command.
Definition: ampsplusplus.hpp:889
AMPS::Command::setBookmark
Command & setBookmark(const std::string &bookmark_)
Set the bookmark to be used this command.
Definition: ampsplusplus.hpp:748
AMPS::Command::reset
Command & reset(const std::string &command_)
Resets the fields of self, and sets the command to command_.
Definition: ampsplusplus.hpp:583
AMPS::ConnectionStateListener
Abstract base class for connection state listeners.
Definition: ampsplusplus.hpp:1511
AMPS::ConnectionStateListener::State
State
Constants for the state of the connection.
Definition: ampsplusplus.hpp:1514
AMPS::ConnectionStateListener::connectionStateChanged
virtual void connectionStateChanged(State newState_)=0
Pure virtual method for receiving the change in connection state.
AMPS::DefaultAuthenticator
A default implementation of Authenticator that only uses an unchanged password and does not implement...
Definition: ampsplusplus.hpp:1038
AMPS::DefaultAuthenticator::authenticate
std::string authenticate(const std::string &, const std::string &password_)
A simple implementation that returns an unmodified password.
Definition: ampsplusplus.hpp:1043
AMPS::DefaultAuthenticator::instance
static Authenticator & instance()
Static function to return a static instance used when no Authenticator is supplied to a Client.
Definition: ampsplusplus.hpp:1060
AMPS::DefaultAuthenticator::retry
std::string retry(const std::string &, const std::string &)
Throws an AuthenticationException because retry is not implemented.
Definition: ampsplusplus.hpp:1050
AMPS::DefaultAuthenticator::completed
void completed(const std::string &, const std::string &, const std::string &)
Called by Client once a logon completes successfully.
Definition: ampsplusplus.hpp:1055
AMPS::ExceptionListener
Exception listener for unhandled exceptions.
Definition: ampsplusplus.hpp:205
AMPS::FIXShredder
Class for parsing a FIX format message into a std::map of keys and values, where the keys and values ...
Definition: ampsplusplus.hpp:9145
AMPS::FIXShredder::map_type
std::map< Message::Field, Message::Field > map_type
Convenience defintion for the std::map specialization used for this class.
Definition: ampsplusplus.hpp:9156
AMPS::FIXShredder::toMap
map_type toMap(const Message::Field &data)
Returns the key/value pairs within the message, represented as AMPS::Field objects that contain point...
Definition: ampsplusplus.hpp:9163
AMPS::FIXShredder::FIXShredder
FIXShredder(char fieldSep_=(char) 1)
Construct an instance of FIXShredder using the specified value as the delimiter between fields.
Definition: ampsplusplus.hpp:9152
AMPS::FailedResubscribeHandler
Abstract base class where you can implement handling of exceptions that occur when a SubscriptionMana...
Definition: ampsplusplus.hpp:1440
AMPS::FailedResubscribeHandler::failure
virtual bool failure(const Message &message_, const MessageHandler &handler_, unsigned requestedAckTypes_, const AMPSException &exception_)=0
Implement this function to return true if the subscription should be removed from the SubscriptionMan...
AMPS::FailedWriteHandler
Class to handle when a client receives a duplicate publish message, or not entitled message.
Definition: ampsplusplus.hpp:1382
AMPS::FailedWriteHandler::failedWrite
virtual void failedWrite(const Message &message_, const char *reason_, size_t reasonLength_)=0
Called when the server indicates a message could not be written.
AMPS::Field
Field represents the value of a single field in a Message.
Definition: Field.hpp:88
AMPS::Field::empty
bool empty() const
Returns 'true' if empty, 'false' otherwise.
Definition: Field.hpp:129
AMPS::Field::len
size_t len() const
Returns the length of the data underlying this field.
Definition: Field.hpp:286
AMPS::Handler< DisconnectHandlerFunc, Client & >
AMPS::MessageStream::iterator
Represents an iterator over messages in an AMPS topic.
Definition: ampsplusplus.hpp:5381
AMPS::MessageStream
An iterable object representing the results of an AMPS subscription and/or query.
Definition: ampsplusplus.hpp:5373
AMPS::MessageStream::begin
iterator begin(void)
Returns an iterator representing the beginning of the topic or subscription.
Definition: ampsplusplus.hpp:5425
AMPS::MessageStream::isValid
bool isValid() const
Returns true if self is a valid stream that may be iterated.
Definition: ampsplusplus.hpp:5418
AMPS::MessageStream::conflate
MessageStream conflate(void)
Sets self to conflation mode, where a new update for a matching sow key will replace the previous one...
Definition: ampsplusplus.hpp:9683
AMPS::MessageStream::maxDepth
MessageStream maxDepth(unsigned maxDepth_)
Sets the maximum number of messages that can be held in the underlying queue.
Definition: ampsplusplus.hpp:9688
AMPS::MessageStream::timeout
MessageStream timeout(unsigned timeout_)
Sets the maximum time to wait for the next message in milliseconds; if no message is available within...
Definition: ampsplusplus.hpp:9678
AMPS::MessageStream::getDepth
unsigned getDepth(void) const
Gets the current number of messages held in the underlying queue.
Definition: ampsplusplus.hpp:9697
AMPS::MessageStream::getMaxDepth
unsigned getMaxDepth(void) const
Gets the maximum number of messages that can be held in the underlying queue.
Definition: ampsplusplus.hpp:9693
AMPS::MessageStream::end
iterator end(void)
Returns an iterator representing the end of the topic or subscription.
Definition: ampsplusplus.hpp:5436
AMPS::Message
Message encapsulates a single message sent to or received from an AMPS server, and provides methods f...
Definition: Message.hpp:518
AMPS::Message::assignExpiration
Message & assignExpiration(const std::string &v)
Assigns the value of the Expiration header for this Message without copying.
Definition: Message.hpp:1345
AMPS::Message::getUserId
Field getUserId() const
Retrieves the value of the UserId header of the Message as a Field which references the underlying bu...
Definition: Message.hpp:1493
AMPS::Message::getBookmark
Field getBookmark() const
Retrieves the value of the Bookmark header of the Message as a Field which references the underlying ...
Definition: Message.hpp:1234
AMPS::Message::setQueryID
Message & setQueryID(const std::string &v)
Sets the value of the QueryID header for this Message.
Definition: Message.hpp:1457
AMPS::Message::getCommandId
Field getCommandId() const
Retrieves the value of the CommandId header of the Message as a Field which references the underlying...
Definition: Message.hpp:1342
AMPS::Message::setExpiration
Message & setExpiration(const std::string &v)
Sets the value of the Expiration header for this Message.
Definition: Message.hpp:1345
AMPS::Message::setMessageType
Message & setMessageType(const std::string &v)
Sets the value of the MessageType header for this Message.
Definition: Message.hpp:1352
AMPS::Message::IdentifierLength
static const unsigned int IdentifierLength
The length of identifiers used for unique identification of commands and subscriptions.
Definition: Message.hpp:528
AMPS::Message::assignOwnershipBookmark
Message & assignOwnershipBookmark(const Field &f)
Assigns the value of the Bookmark header for this Message without copying and makes this Message resp...
Definition: Message.hpp:1234
AMPS::Message::assignAckType
Message & assignAckType(const std::string &v)
Assigns the value of the AckType header for this Message without copying.
Definition: Message.hpp:1170
AMPS::Message::assignSubscriptionId
Message & assignSubscriptionId(const std::string &v)
Assigns the value of the SubscriptionId header for this Message without copying.
Definition: Message.hpp:1467
AMPS::Message::deepCopy
Message deepCopy(void) const
Returns a deep copy of self.
Definition: Message.hpp:565
AMPS::Message::newCommandId
Message & newCommandId()
Creates and sets a new sequential value for the CommandId header for this Message.
Definition: Message.hpp:1342
AMPS::Message::getSequence
Field getSequence() const
Retrieves the value of the Sequence header of the Message as a Field which references the underlying ...
Definition: Message.hpp:1462
AMPS::Message::setBookmark
Message & setBookmark(const std::string &v)
Sets the value of the Bookmark header for this Message.
Definition: Message.hpp:1234
AMPS::Message::getSubscriptionId
Field getSubscriptionId() const
Retrieves the value of the SubscriptionId header of the Message as a Field which references the under...
Definition: Message.hpp:1467
AMPS::Message::setClientName
Message & setClientName(const std::string &v)
Sets the value of the ClientName header for this Message.
Definition: Message.hpp:1343
AMPS::Message::assignFilter
Message & assignFilter(const std::string &v)
Assigns the value of the Filter header for this Message without copying.
Definition: Message.hpp:1346
AMPS::Message::setSequence
Message & setSequence(const std::string &v)
Sets the value of the Sequence header for this Message.
Definition: Message.hpp:1462
AMPS::Message::setOrderBy
Message & setOrderBy(const std::string &v)
Sets the value of the OrderBy header for this Message.
Definition: Message.hpp:1455
AMPS::Message::setUserId
Message & setUserId(const std::string &v)
Sets the value of the UserId header for this Message.
Definition: Message.hpp:1493
AMPS::Message::setPassword
Message & setPassword(const std::string &v)
Sets the value of the Password header for this Message.
Definition: Message.hpp:1456
AMPS::Message::setCommandId
Message & setCommandId(const std::string &v)
Sets the value of the CommandId header for this Message.
Definition: Message.hpp:1342
AMPS::Message::setAckTypeEnum
Message & setAckTypeEnum(unsigned ackType_)
Encode self's "ack type" field from a bitmask of values from AckType.
Definition: Message.hpp:1223
AMPS::Message::getAckTypeEnum
unsigned getAckTypeEnum() const
Decode self's "ack type" field and return the corresponding bitmask of values from AckType.
Definition: Message.hpp:1200
AMPS::Message::assignVersion
Message & assignVersion(const std::string &v)
Assigns the value of the Version header for this Message without copying.
Definition: Message.hpp:1492
AMPS::Message::assignCommand
Message & assignCommand(const std::string &v)
Assigns the value of the Command header for this Message without copying.
Definition: Message.hpp:1235
AMPS::Message::setQueryId
Message & setQueryId(const std::string &v)
Sets the value of the QueryID header for this Message.
Definition: Message.hpp:1457
AMPS::Message::setTopNRecordsReturned
Message & setTopNRecordsReturned(const std::string &v)
Sets the value of the TopNRecordsReturned header for this Message.
Definition: Message.hpp:1491
AMPS::Message::assignSowKeys
Message & assignSowKeys(const std::string &v)
Assigns the value of the SowKeys header for this Message without copying.
Definition: Message.hpp:1465
AMPS::Message::setAckType
Message & setAckType(const std::string &v)
Sets the value of the AckType header for this Message.
Definition: Message.hpp:1170
AMPS::Message::setSowKey
Message & setSowKey(const std::string &v)
Sets the value of the SowKey header for this Message.
Definition: Message.hpp:1464
AMPS::Message::setOptions
Message & setOptions(const std::string &v)
Sets the value of the Options header for this Message.
Definition: Message.hpp:1384
AMPS::Message::getCommandEnum
Command::Type getCommandEnum() const
Decode self's "command" field and return one of the values from Command.
Definition: Message.hpp:1268
AMPS::Message::getPassword
Field getPassword() const
Retrieves the value of the Password header of the Message as a Field which references the underlying ...
Definition: Message.hpp:1456
AMPS::Message::assignQueryID
Message & assignQueryID(const std::string &v)
Assigns the value of the QueryID header for this Message without copying.
Definition: Message.hpp:1457
AMPS::Message::assignTopic
Message & assignTopic(const std::string &v)
Assigns the value of the Topic header for this Message without copying.
Definition: Message.hpp:1489
AMPS::Message::setTopic
Message & setTopic(const std::string &v)
Sets the value of the Topic header for this Message.
Definition: Message.hpp:1489
AMPS::Message::setCommandEnum
Message & setCommandEnum(Command::Type command_)
Set self's "command" field from one of the values in Command.
Definition: Message.hpp:1328
AMPS::Message::setSubscriptionId
Message & setSubscriptionId(const std::string &v)
Sets the value of the SubscriptionId header for this Message.
Definition: Message.hpp:1467
AMPS::Message::setFilter
Message & setFilter(const std::string &v)
Sets the value of the Filter header for this Message.
Definition: Message.hpp:1346
AMPS::Message::getAckType
Field getAckType() const
Retrieves the value of the AckType header of the Message as a Field which references the underlying b...
Definition: Message.hpp:1170
AMPS::Message::getQueryId
Field getQueryId() const
Retrieves the value of the QueryID header of the Message as a Field which references the underlying b...
Definition: Message.hpp:1457
AMPS::Message::assignSequence
Message & assignSequence(const std::string &v)
Assigns the value of the Sequence header for this Message without copying.
Definition: Message.hpp:1462
AMPS::Message::getTopic
Field getTopic() const
Retrieves the value of the Topic header of the Message as a Field which references the underlying buf...
Definition: Message.hpp:1489
AMPS::Message::assignCorrelationId
Message & assignCorrelationId(const std::string &v)
Assigns the value of the CorrelationId header for this Message without copying.
Definition: Message.hpp:1344
AMPS::Message::getQueryID
Field getQueryID() const
Retrieves the value of the QueryID header of the Message as a Field which references the underlying b...
Definition: Message.hpp:1457
AMPS::Message::setSowKeys
Message & setSowKeys(const std::string &v)
Sets the value of the SowKeys header for this Message.
Definition: Message.hpp:1465
AMPS::Message::setBatchSize
Message & setBatchSize(const std::string &v)
Sets the value of the BatchSize header for this Message.
Definition: Message.hpp:1233
AMPS::Message::setCommand
Message & setCommand(const std::string &v)
Sets the value of the Command header for this Message.
Definition: Message.hpp:1235
AMPS::Message::getOptions
Field getOptions() const
Retrieves the value of the Options header of the Message as a Field which references the underlying b...
Definition: Message.hpp:1356
AMPS::Message::setCorrelationId
Message & setCorrelationId(const std::string &v)
Sets the value of the CorrelationId header for this Message.
Definition: Message.hpp:1344
AMPS::Message::setData
Message & setData(const std::string &v_)
Sets the data portion of self.
Definition: Message.hpp:1516
AMPS::Message::assignSowKey
Message & assignSowKey(const std::string &v)
Assigns the value of the SowKey header for this Message without copying.
Definition: Message.hpp:1464
AMPS::Reason
Class to hold string versions of failure reasons.
Definition: ampsplusplus.hpp:152
AMPS::StoreImpl
Abstract base class for storing published messages for an HA publisher client.
Definition: ampsplusplus.hpp:1098
AMPS::StoreImpl::getLowestUnpersisted
virtual amps_uint64_t getLowestUnpersisted() const =0
Get the oldest unpersisted message sequence in the store.
AMPS::StoreImpl::unpersistedCount
virtual size_t unpersistedCount() const =0
Method to return how many messages are in the store that have not been discarded, indicating that the...
AMPS::StoreImpl::getUnsetSequence
static amps_uint64_t getUnsetSequence()
Method to return the value used to represent no such sequence.
Definition: ampsplusplus.hpp:1167
AMPS::StoreImpl::getLastPersisted
virtual amps_uint64_t getLastPersisted()=0
Get the last persisted sequence number.
AMPS::StoreImpl::getUnsetPosition
static size_t getUnsetPosition()
Method to return the value used to represent not found or unset.
Definition: ampsplusplus.hpp:1160
AMPS::StoreImpl::discardUpTo
virtual void discardUpTo(amps_uint64_t index_)=0
Called by Client to indicate that all messages up to and including.
AMPS::StoreImpl::flush
virtual void flush(long timeout_)=0
Method to wait for the Store to discard everything that has been stored up to the point in time when ...
AMPS::StoreImpl::store
virtual amps_uint64_t store(const Message &message_)=0
Called by Client to store a message being published.
AMPS::StoreImpl::replay
virtual void replay(StoreReplayer &replayer_)=0
Called by Client to get all stored and non-discarded messages replayed by the store onto the StoreRep...
AMPS::StoreImpl::replaySingle
virtual bool replaySingle(StoreReplayer &replayer_, amps_uint64_t index_)=0
Called by Client to get a single message replayed by the store onto the StoreReplayer.
AMPS::StoreImpl::StoreImpl
StoreImpl(bool errorOnPublishGap_=false)
Default constructor.
Definition: ampsplusplus.hpp:1105
AMPS::StoreImpl::setResizeHandler
virtual void setResizeHandler(PublishStoreResizeHandler handler_, void *userData_)
Set a handler to be called if the Store needs to resize in order to keep storing messages.
Definition: ampsplusplus.hpp:1191
AMPS::StoreReplayer
Abstract base class for replaying a publish message.
Definition: ampsplusplus.hpp:1070
AMPS::StoreReplayer::execute
virtual void execute(Message &message_)=0
Called by implementations of Store to replay a message from the store.
AMPS::Store
Handle class for StoreImpl classes that track publish messages.
Definition: ampsplusplus.hpp:1224
AMPS::Store::replay
void replay(StoreReplayer &replayer_)
Called by Client to get all stored and non-discarded messages replayed by the store onto the StoreRep...
Definition: ampsplusplus.hpp:1259
AMPS::Store::get
StoreImpl * get()
Used to get a pointer to the implementation.
Definition: ampsplusplus.hpp:1363
AMPS::Store::getErrorOnPublishGap
bool getErrorOnPublishGap() const
Called to check if the Store will throw PublishStoreGapException.
Definition: ampsplusplus.hpp:1355
AMPS::Store::unpersistedCount
size_t unpersistedCount() const
Method to return how many messages are in the store that have not been discarded, indicating that the...
Definition: ampsplusplus.hpp:1280
AMPS::Store::isValid
bool isValid() const
Method to return if there is an underlying implementation for the Store.
Definition: ampsplusplus.hpp:1288
AMPS::Store::getLastPersisted
amps_uint64_t getLastPersisted()
Get the last persisted message sequence in the store.
Definition: ampsplusplus.hpp:1317
AMPS::Store::discardUpTo
void discardUpTo(amps_uint64_t index_)
Called by Client to indicate that all messages up to and including.
Definition: ampsplusplus.hpp:1250
AMPS::Store::getLowestUnpersisted
amps_uint64_t getLowestUnpersisted()
Get the oldest unpersisted message sequence in the store.
Definition: ampsplusplus.hpp:1309
AMPS::Store::flush
void flush(long timeout_=0)
Method to wait for the Store to discard everything that has been stored up to the point in time when ...
Definition: ampsplusplus.hpp:1301
AMPS::Store::setResizeHandler
void setResizeHandler(PublishStoreResizeHandler handler_, void *userData_)
Set a handler to be called if the Store needs to resize in order to keep storing messages.
Definition: ampsplusplus.hpp:1331
AMPS::Store::replaySingle
bool replaySingle(StoreReplayer &replayer_, amps_uint64_t index_)
Called by Client to get a single message replayed by the store onto the StoreReplayer.
Definition: ampsplusplus.hpp:1271
AMPS::Store::setErrorOnPublishGap
void setErrorOnPublishGap(bool errorOnPublishGap_)
Called to enable or disable throwing PublishStoreGapException.
Definition: ampsplusplus.hpp:1346
AMPS::Store::store
amps_uint64_t store(const Message &message_)
Called by Client to store a message being published.
Definition: ampsplusplus.hpp:1239
AMPS::SubscriptionManager
Abstract base class to manage all subscriptions placed on a client so that they can be re-established...
Definition: ampsplusplus.hpp:1462
AMPS::SubscriptionManager::resubscribe
virtual void resubscribe(Client &client_)=0
Called by Client to get all subscriptions placed again.
AMPS::SubscriptionManager::getGenerationCount
virtual AMPS_ATOMIC_BASE_TYPE getGenerationCount() const
Gets the current subscription manager generation count.
Definition: ampsplusplus.hpp:1479
AMPS::SubscriptionManager::setFailedResubscribeHandler
virtual void setFailedResubscribeHandler(std::shared_ptr< FailedResubscribeHandler > handler_)
Set a handler to deal with failing subscriptions after a failover event.
Definition: ampsplusplus.hpp:1498
AMPS::SubscriptionManager::unsubscribe
virtual void unsubscribe(const Message::Field &subId_)=0
Called by Client when a subscription is unsubscribed.
AMPS::SubscriptionManager::clear
virtual void clear()=0
Clear subscriptions and reset to the initial state.
AMPS::SubscriptionManager::subscribe
virtual AMPS_ATOMIC_BASE_TYPE subscribe(MessageHandler messageHandler_, const Message &message_, unsigned requestedAckTypes_)=0
Called by Client when a subscription is placed.
AMPS::_FIXBuilder
Provides a convenient way of building messages in FIX format, typically referenced using the typedefs...
Definition: ampsplusplus.hpp:9071
AMPS::_FIXBuilder::_FIXBuilder
_FIXBuilder(char fieldSep_=(char) 1)
Construct an instance of _FIXBuilder, using the specified separator between fields.
Definition: ampsplusplus.hpp:9080
AMPS::_FIXBuilder::getString
std::string getString() const
Returns the current contents of this builder as a string.
Definition: ampsplusplus.hpp:9107
AMPS::_FIXBuilder::append
void append(const T &tag, const std::string &value)
Write a field with the provided tag and value to the message being constructed.
Definition: ampsplusplus.hpp:9100
AMPS::_FIXBuilder::reset
void reset()
Clear all data from the builder.
Definition: ampsplusplus.hpp:9117
AMPS::_FIXBuilder::append
void append(const T &tag, const char *value, size_t offset, size_t length)
Write a field with the provided tag and value to the message being constructed.
Definition: ampsplusplus.hpp:9089
AMPS::Client::sowDelete
std::string sowDelete(const MessageHandler &messageHandler, const std::string &topic, const std::string &filter, long timeout)
Deletes one or more messages from a topic's SOW cache.
Definition: ampsplusplus.hpp:7558
AMPS::Client::sowDeleteByData
Message sowDeleteByData(const std::string &topic_, const std::string &data_, long timeout_=0)
Deletes the message whose keys match the message data provided.
Definition: ampsplusplus.hpp:7738
AMPS::Client::sowAndSubscribe
std::string sowAndSubscribe(const MessageHandler &messageHandler_, const std::string &topic_, const std::string &filter_="", const std::string &orderBy_="", const std::string &bookmark_="", int batchSize_=DEFAULT_BATCH_SIZE, int topN_=DEFAULT_TOP_N, const std::string &options_="", long timeout_=DEFAULT_COMMAND_TIMEOUT)
Query the SOW cache of a topic and initiates a new subscription on it.
Definition: ampsplusplus.hpp:7234
AMPS::Client::sow
std::string sow(const MessageHandler &messageHandler_, const std::string &topic_, long timeout_, const std::string &filter_="", int batchSize_=DEFAULT_BATCH_SIZE, int topN_=DEFAULT_TOP_N)
Query the SOW cache of a topic.
Definition: ampsplusplus.hpp:7084
AMPS::Client::sowAndDeltaSubscribe
std::string sowAndDeltaSubscribe(const MessageHandler &messageHandler_, const std::string &topic_, long timeout_, const std::string &filter_="", int batchSize_=DEFAULT_BATCH_SIZE, bool oofEnabled_=false, bool sendEmpties_=false, int topN_=DEFAULT_TOP_N)
Query the SOW cache of a topic and initiates a new delta subscription on it.
Definition: ampsplusplus.hpp:7442
AMPS::Client::sowDeleteByData
std::string sowDeleteByData(const MessageHandler &messageHandler_, const std::string &topic_, const std::string &data_, long timeout_=0)
Deletes the message whose keys match the message data provided.
Definition: ampsplusplus.hpp:7717
AMPS::Client::sowAndSubscribe
std::string sowAndSubscribe(const MessageHandler &messageHandler_, const std::string &topic_, long timeout_, const std::string &filter_="", int batchSize_=DEFAULT_BATCH_SIZE, bool oofEnabled_=false, int topN_=DEFAULT_TOP_N)
Query the SOW cache of a topic and initiates a new subscription on it.
Definition: ampsplusplus.hpp:7116
AMPS::Client::sowDeleteByKeys
std::string sowDeleteByKeys(const MessageHandler &messageHandler_, const std::string &topic_, const std::string &keys_, long timeout_=0)
Deletes messages that match SOW keys from a topic's SOW cache.
Definition: ampsplusplus.hpp:7649
AMPS::Client::sowAndDeltaSubscribe
std::string sowAndDeltaSubscribe(const MessageHandler &messageHandler_, const std::string &topic_, const std::string &filter_="", const std::string &orderBy_="", int batchSize_=DEFAULT_BATCH_SIZE, int topN_=DEFAULT_TOP_N, const std::string &options_="", long timeout_=DEFAULT_COMMAND_TIMEOUT)
Query the SOW cache of a topic and initiates a new delta subscription on it.
Definition: ampsplusplus.hpp:7342
AMPS::Client::sow
std::string sow(const MessageHandler &messageHandler_, const std::string &topic_, const std::string &filter_="", const std::string &orderBy_="", const std::string &bookmark_="", int batchSize_=DEFAULT_BATCH_SIZE, int topN_=DEFAULT_TOP_N, const std::string &options_="", long timeout_=DEFAULT_COMMAND_TIMEOUT)
Query a State-of-the-World topic.
Definition: ampsplusplus.hpp:6984
AMPS_BOOKMARK_NOW
#define AMPS_BOOKMARK_NOW
Start the subscription at the point in time when AMPS processes the subscription.
Definition: BookmarkStore.hpp:55
AMPS_BOOKMARK_EPOCH
#define AMPS_BOOKMARK_EPOCH
Start the subscription at the beginning of the journal.
Definition: BookmarkStore.hpp:51
AMPS_BOOKMARK_RECENT
#define AMPS_BOOKMARK_RECENT
Start the subscription at the first undiscarded message in the bookmark store, or at the end of the b...
Definition: BookmarkStore.hpp:47
AMPS::BookmarkStore::get
BookmarkStoreImpl * get()
Used to get a pointer to the implementation.
Definition: BookmarkStore.hpp:465
AMPS::Client::sowAndDeltaSubscribe
MessageStream sowAndDeltaSubscribe(const char *topic_, long timeout_, const std::string &filter_="", int batchSize_=DEFAULT_BATCH_SIZE, bool oofEnabled_=false, bool sendEmpties_=false, int topN_=DEFAULT_TOP_N)
Query the SOW cache of a topic and initiates a new delta subscription on it.
Definition: ampsplusplus.hpp:7519
AMPS::Client::sowAndDeltaSubscribe
MessageStream sowAndDeltaSubscribe(const std::string &topic_, const std::string &filter_="", const std::string &orderBy_="", int batchSize_=DEFAULT_BATCH_SIZE, int topN_=DEFAULT_TOP_N, const std::string &options_="", long timeout_=DEFAULT_COMMAND_TIMEOUT)
Query the SOW cache of a topic and initiates a new delta subscription on it.
Definition: ampsplusplus.hpp:7375
AMPS::Client::sowDeleteByKeys
Message sowDeleteByKeys(const std::string &topic_, const std::string &keys_, long timeout_=0)
Deletes messages that match SOW keys from a topic's SOW cache.
Definition: ampsplusplus.hpp:7676
AMPS::Client::sow
MessageStream sow(const std::string &topic_, const std::string &filter_="", const std::string &orderBy_="", const std::string &bookmark_="", int batchSize_=DEFAULT_BATCH_SIZE, int topN_=DEFAULT_TOP_N, const std::string &options_="", long timeout_=DEFAULT_COMMAND_TIMEOUT)
Query the SOW cache of a topic.
Definition: ampsplusplus.hpp:7022
AMPS::Client::sowAndSubscribe
MessageStream sowAndSubscribe(const std::string &topic_, long timeout_, const std::string &filter_="", int batchSize_=DEFAULT_BATCH_SIZE, bool oofEnabled_=false, int topN_=DEFAULT_TOP_N)
Query the SOW cache of a topic and initiates a new subscription on it.
Definition: ampsplusplus.hpp:7148
AMPS::Client::sowAndSubscribe
MessageStream sowAndSubscribe(const char *topic_, long timeout_, const std::string &filter_="", int batchSize_=DEFAULT_BATCH_SIZE, bool oofEnabled_=false, int topN_=DEFAULT_TOP_N)
Query the SOW cache of a topic and initiates a new subscription on it.
Definition: ampsplusplus.hpp:7186
AMPS::Client::sowDelete
Message sowDelete(const std::string &topic_, const std::string &filter_, long timeout_=0)
Deletes one or more messages from a topic's SOW cache.
Definition: ampsplusplus.hpp:7581
AMPS::Client::sowAndDeltaSubscribe
MessageStream sowAndDeltaSubscribe(const std::string &topic_, long timeout_, const std::string &filter_="", int batchSize_=DEFAULT_BATCH_SIZE, bool oofEnabled_=false, bool sendEmpties_=false, int topN_=DEFAULT_TOP_N)
Query the SOW cache of a topic and initiates a new delta subscription on it.
Definition: ampsplusplus.hpp:7478
AMPS::Client::sowAndSubscribe
MessageStream sowAndSubscribe(const std::string &topic_, const std::string &filter_="", const std::string &orderBy_="", const std::string &bookmark_="", int batchSize_=DEFAULT_BATCH_SIZE, int topN_=DEFAULT_TOP_N, const std::string &options_="", long timeout_=DEFAULT_COMMAND_TIMEOUT)
Query the SOW cache of a topic and initiates a new subscription on it.
Definition: ampsplusplus.hpp:7273