AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.4.0
ampsplusplus.hpp
Go to the documentation of this file.
1 //
3 // Copyright (c) 2010-2023 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 <set>
39 #include <deque>
40 #include <vector>
41 #include <assert.h>
42 #ifndef _WIN32
43  #include <inttypes.h>
44 #endif
45 #if defined(sun)
46  #include <sys/atomic.h>
47 #endif
48 #include "amps/BookmarkStore.hpp"
49 #include "amps/MessageRouter.hpp"
50 #include "amps/util.hpp"
51 #include "amps/ampscrc.hpp"
52 #if __cplusplus >= 201100L || _MSC_VER >= 1900
53 #include <atomic>
54 #endif
55 
56 #ifndef AMPS_TESTING_SLOW_MESSAGE_STREAM
57  #define AMPS_TESTING_SLOW_MESSAGE_STREAM
58 #endif
59 
64 
65 
72 
83 
84 // For StoreBuffer implementations
85 #define AMPS_MEMORYBUFFER_DEFAULT_BUFFERS 10
86 #define AMPS_MEMORYBUFFER_DEFAULT_LENGTH 40960
87 #define AMPS_SUBSCRIPTION_MANAGER_DEFAULT_TIMEOUT 0
88 #define AMPS_HACLIENT_TIMEOUT_DEFAULT 10000
89 #define AMPS_HACLIENT_RECONNECT_DEFAULT 200
90 #define AMPS_DEFAULT_COMMAND_TIMEOUT 5000
91 #define AMPS_DEFAULT_TOP_N -1
92 #define AMPS_DEFAULT_BATCH_SIZE 10
93 #define AMPS_NUMBER_BUFFER_LEN 20
94 #define AMPS_DEFAULT_QUEUE_ACK_TIMEOUT 1000
95 
96 #if defined(_M_X64) || defined(__x86_64) || defined(_WIN64)
97  #define AMPS_X64 1
98 #endif
99 
100 #ifdef _WIN32
101  static __declspec ( thread ) AMPS::Message* publishStoreMessage = 0;
102 #else
103  static __thread AMPS::Message* publishStoreMessage = 0;
104 #endif
105 
106 namespace AMPS
107 {
108 
109  typedef std::map<std::string, std::string> ConnectionInfo;
110 
111  class PerThreadMessageTracker
112  {
113  std::vector<AMPS::Message*> _messages;
114  public:
115  PerThreadMessageTracker() {}
116  ~PerThreadMessageTracker()
117  {
118  for (size_t i = 0; i < _messages.size(); ++i)
119  {
120  delete _messages[i];
121  }
122  }
123  void addMessage(AMPS::Message* message)
124  {
125  _messages.push_back(message);
126  }
127  static void addMessageToCleanupList(AMPS::Message* message)
128  {
129  static AMPS::Mutex _lock;
130  AMPS::Lock<Mutex> l(_lock);
131  _addMessageToCleanupList(message);
132  }
133  static void _addMessageToCleanupList(AMPS::Message* message)
134  {
135  static PerThreadMessageTracker tracker;
136  tracker.addMessage(message);
137  }
138  };
139 
140  template<class Type>
141  inline std::string asString(Type x_)
142  {
143  std::ostringstream os;
144  os << x_;
145  return os.str();
146  }
147 
148  inline
149  size_t convertToCharArray(char* buf_, amps_uint64_t seqNo_)
150  {
151  size_t pos = AMPS_NUMBER_BUFFER_LEN;
152  for (int i = 0; i < AMPS_NUMBER_BUFFER_LEN; ++i)
153  {
154  if (seqNo_ > 0)
155  {
156  buf_[--pos] = (char)(seqNo_ % 10 + '0');
157  seqNo_ /= 10;
158  }
159  }
160  return pos;
161  }
162 
163 #ifdef _WIN32
164  inline
165  size_t convertToCharArray(char* buf_, unsigned long seqNo_)
166  {
167  size_t pos = AMPS_NUMBER_BUFFER_LEN;
168  for (int i = 0; i < AMPS_NUMBER_BUFFER_LEN; ++i)
169  {
170  if (seqNo_ > 0)
171  {
172  buf_[--pos] = (char)(seqNo_ % 10 + '0');
173  seqNo_ /= 10;
174  }
175  }
176  return pos;
177  }
178 #endif
179 
183  class Reason
184  {
185  public:
186  static const char* duplicate()
187  {
188  return "duplicate";
189  }
190  static const char* badFilter()
191  {
192  return "bad filter";
193  }
194  static const char* badRegexTopic()
195  {
196  return "bad regex topic";
197  }
198  static const char* subscriptionAlreadyExists()
199  {
200  return "subscription already exists";
201  }
202  static const char* nameInUse()
203  {
204  return "name in use";
205  }
206  static const char* authFailure()
207  {
208  return "auth failure";
209  }
210  static const char* notEntitled()
211  {
212  return "not entitled";
213  }
214  static const char* authDisabled()
215  {
216  return "authentication disabled";
217  }
218  static const char* subidInUse()
219  {
220  return "subid in use";
221  }
222  static const char* noTopic()
223  {
224  return "no topic";
225  }
226  };
227 
237  {
238  public:
239  virtual ~ExceptionListener() {;}
240  virtual void exceptionThrown(const std::exception&) const {;}
241  };
242 
244 
245 
246 #define AMPS_CALL_EXCEPTION_WRAPPER(x) \
247  try\
248  {\
249  x;\
250  }\
251  catch (std::exception& ex_)\
252  {\
253  try\
254  {\
255  _exceptionListener->exceptionThrown(ex_);\
256  }\
257  catch(...)\
258  {\
259  ;\
260  }\
261  }
262  /*
263  * Note : we don't attempt to trap non std::exception exceptions
264  * here because doing so interferes with pthread_exit on some OSes.
265  catch (...)\
266  {\
267  try\
268  {\
269  _exceptionListener->exceptionThrown(AMPS::AMPSException(\
270  "An unhandled exception of unknown type was thrown by "\
271  "the registered handler.", AMPS_E_USAGE));\
272  }\
273  catch(...)\
274  {\
275  ;\
276  }\
277  }
278  */
279 #ifdef _WIN32
280 #define AMPS_CALL_EXCEPTION_WRAPPER_2(me,x) \
281  try\
282  {\
283  while(me->_connected)\
284  {\
285  try\
286  {\
287  x;\
288  break;\
289  }\
290  catch(MessageStreamFullException&)\
291  {\
292  me->checkAndSendHeartbeat(false);\
293  }\
294  }\
295  }\
296  catch (std::exception& ex_)\
297  {\
298  try\
299  {\
300  me->_exceptionListener->exceptionThrown(ex_);\
301  }\
302  catch(...)\
303  {\
304  ;\
305  }\
306  }
307  /*
308  * Note : we don't attempt to trap non std::exception exceptions
309  * here because doing so interferes with pthread_exit on some OSes.
310  catch (...)\
311  {\
312  try\
313  {\
314  me->_exceptionListener->exceptionThrown(AMPS::AMPSException(\
315  "An unhandled exception of unknown type was thrown by "\
316  "the registered handler.", AMPS_E_USAGE));\
317  }\
318  catch(...)\
319  {\
320  ;\
321  }\
322  }*/
323 
324 #define AMPS_CALL_EXCEPTION_WRAPPER_STREAM_FULL_2(me, x)\
325  while(me->_connected)\
326  {\
327  try\
328  {\
329  x;\
330  break;\
331  }\
332  catch(MessageStreamFullException&)\
333  {\
334  me->checkAndSendHeartbeat(false);\
335  }\
336  }
337 #else
338 #define AMPS_CALL_EXCEPTION_WRAPPER_2(me,x) \
339  try\
340  {\
341  while(me->_connected)\
342  {\
343  try\
344  {\
345  x;\
346  break;\
347  }\
348  catch(MessageStreamFullException& ex_)\
349  {\
350  me->checkAndSendHeartbeat(false);\
351  }\
352  }\
353  }\
354  catch (std::exception& ex_)\
355  {\
356  try\
357  {\
358  me->_exceptionListener->exceptionThrown(ex_);\
359  }\
360  catch(...)\
361  {\
362  ;\
363  }\
364  }
365  /*
366  * Note : we don't attempt to trap non std::exception exceptions
367  * here because doing so interferes with pthread_exit on some OSes.
368  catch (...)\
369  {\
370  try\
371  {\
372  me->_exceptionListener->exceptionThrown(AMPS::AMPSException(\
373  "An unhandled exception of unknown type was thrown by "\
374  "the registered handler.", AMPS_E_USAGE));\
375  }\
376  catch(...)\
377  {\
378  ;\
379  }\
380  }*/
381 
382 #define AMPS_CALL_EXCEPTION_WRAPPER_STREAM_FULL_2(me, x)\
383  while(me->_connected)\
384  {\
385  try\
386  {\
387  x;\
388  break;\
389  }\
390  catch(MessageStreamFullException& ex_)\
391  {\
392  me->checkAndSendHeartbeat(false);\
393  }\
394  }
395 #endif
396 
397 #define AMPS_UNHANDLED_EXCEPTION(ex) \
398  try\
399  {\
400  _exceptionListener->exceptionThrown(ex);\
401  }\
402  catch(...)\
403  {;}
404 
405 #define AMPS_UNHANDLED_EXCEPTION_2(me,ex) \
406  try\
407  {\
408  me->_exceptionListener->exceptionThrown(ex);\
409  }\
410  catch(...)\
411  {;}
412 
413 
414  class Client;
415 
440 
441  class Command
442  {
443  Message _message;
444  unsigned _timeout;
445  unsigned _batchSize;
446  unsigned _flags;
447  static const unsigned Subscribe = 1;
448  static const unsigned SOW = 2;
449  static const unsigned NeedsSequenceNumber = 4;
450  static const unsigned ProcessedAck = 8;
451  static const unsigned StatsAck = 16;
452  void init(Message::Command::Type command_)
453  {
454  _timeout = 0;
455  _batchSize = 0;
456  _flags = 0;
457  _message.reset();
458  _message.setCommandEnum(command_);
459  _setIds();
460  }
461  void init(const std::string& command_)
462  {
463  _timeout = 0;
464  _batchSize = 0;
465  _flags = 0;
466  _message.reset();
467  _message.setCommand(command_);
468  _setIds();
469  }
470  void init(const char* command_, size_t commandLen_)
471  {
472  _timeout = 0;
473  _batchSize = 0;
474  _flags = 0;
475  _message.reset();
476  _message.setCommand(command_, commandLen_);
477  _setIds();
478  }
479  void _setIds(void)
480  {
481  Message::Command::Type command = _message.getCommandEnum();
482  if (!(command & Message::Command::NoDataCommands))
483  {
484  _message.newCommandId();
485  if (command == Message::Command::Subscribe ||
486  command == Message::Command::SOWAndSubscribe ||
487  command == Message::Command::DeltaSubscribe ||
488  command == Message::Command::SOWAndDeltaSubscribe)
489  {
490  _message.setSubscriptionId(_message.getCommandId());
491  _flags |= Subscribe;
492  }
493  if (command == Message::Command::SOW
494  || command == Message::Command::SOWAndSubscribe
495  || command == Message::Command::SOWAndDeltaSubscribe)
496  {
497  _message.setQueryID(_message.getCommandId());
498  if (_batchSize == 0)
499  {
500  setBatchSize(AMPS_DEFAULT_BATCH_SIZE);
501  }
502  if (command == Message::Command::SOW)
503  {
504  _flags |= SOW;
505  }
506  }
507  _flags |= ProcessedAck;
508  }
509  else if (command == Message::Command::SOWDelete)
510  {
511  _message.newCommandId();
512  _flags |= ProcessedAck;
513  _flags |= NeedsSequenceNumber;
514  }
515  else if (command == Message::Command::Publish
516  || command == Message::Command::DeltaPublish)
517  {
518  _flags |= NeedsSequenceNumber;
519  }
520  else if (command == Message::Command::StopTimer)
521  {
522  _message.newCommandId();
523  }
524  }
525  public:
529  Command(const std::string& command_)
530  {
531  init(command_);
532  }
537  Command(const char* command_, size_t commandLen_)
538  {
539  init(command_, commandLen_);
540  }
544  Command(Message::Command::Type command_)
545  {
546  init(command_);
547  }
548 
552  Command& reset(const std::string& command_)
553  {
554  init(command_);
555  return *this;
556  }
561  Command& reset(const char* command_, size_t commandLen_)
562  {
563  init(command_, commandLen_);
564  return *this;
565  }
569  Command& reset(Message::Command::Type command_)
570  {
571  init(command_);
572  return *this;
573  }
581  Command& setSowKey(const std::string& sowKey_)
582  {
583  _message.setSowKey(sowKey_);
584  return *this;
585  }
594  Command& setSowKey(const char* sowKey_, size_t sowKeyLen_)
595  {
596  _message.setSowKey(sowKey_, sowKeyLen_);
597  return *this;
598  }
611  Command& setSowKeys(const std::string& sowKeys_)
612  {
613  _message.setSowKeys(sowKeys_);
614  return *this;
615  }
629  Command& setSowKeys(const char* sowKeys_, size_t sowKeysLen_)
630  {
631  _message.setSowKeys(sowKeys_, sowKeysLen_);
632  return *this;
633  }
635  Command& setCommandId(const std::string& cmdId_)
636  {
637  _message.setCommandId(cmdId_);
638  return *this;
639  }
642  Command& setCommandId(const char* cmdId_, size_t cmdIdLen_)
643  {
644  _message.setCommandId(cmdId_, cmdIdLen_);
645  return *this;
646  }
648  Command& setTopic(const std::string& topic_)
649  {
650  _message.setTopic(topic_);
651  return *this;
652  }
655  Command& setTopic(const char* topic_, size_t topicLen_)
656  {
657  _message.setTopic(topic_, topicLen_);
658  return *this;
659  }
661  Command& setFilter(const std::string& filter_)
662  {
663  _message.setFilter(filter_);
664  return *this;
665  }
668  Command& setFilter(const char* filter_, size_t filterLen_)
669  {
670  _message.setFilter(filter_, filterLen_);
671  return *this;
672  }
674  Command& setOrderBy(const std::string& orderBy_)
675  {
676  _message.setOrderBy(orderBy_);
677  return *this;
678  }
681  Command& setOrderBy(const char* orderBy_, size_t orderByLen_)
682  {
683  _message.setOrderBy(orderBy_, orderByLen_);
684  return *this;
685  }
687  Command& setSubId(const std::string& subId_)
688  {
689  _message.setSubscriptionId(subId_);
690  return *this;
691  }
694  Command& setSubId(const char* subId_, size_t subIdLen_)
695  {
696  _message.setSubscriptionId(subId_, subIdLen_);
697  return *this;
698  }
700  Command& setQueryId(const std::string& queryId_)
701  {
702  _message.setQueryId(queryId_);
703  return *this;
704  }
707  Command& setQueryId(const char* queryId_, size_t queryIdLen_)
708  {
709  _message.setQueryId(queryId_, queryIdLen_);
710  return *this;
711  }
717  Command& setBookmark(const std::string& bookmark_)
718  {
719  _message.setBookmark(bookmark_);
720  return *this;
721  }
728  Command& setBookmark(const char* bookmark_, size_t bookmarkLen_)
729  {
730  _message.setBookmark(bookmark_, bookmarkLen_);
731  return *this;
732  }
739  Command& setCorrelationId(const std::string& correlationId_)
740  {
741  _message.setCorrelationId(correlationId_);
742  return *this;
743  }
751  Command& setCorrelationId(const char* correlationId_, size_t correlationIdLen_)
752  {
753  _message.setCorrelationId(correlationId_, correlationIdLen_);
754  return *this;
755  }
758  Command& setOptions(const std::string& options_)
759  {
760  _message.setOptions(options_);
761  return *this;
762  }
766  Command& setOptions(const char* options_, size_t optionsLen_)
767  {
768  _message.setOptions(options_, optionsLen_);
769  return *this;
770  }
772  Command& setSequence(const std::string& seq_)
773  {
774  _message.setSequence(seq_);
775  return *this;
776  }
779  Command& setSequence(const char* seq_, size_t seqLen_)
780  {
781  _message.setSequence(seq_, seqLen_);
782  return *this;
783  }
785  Command& setSequence(const amps_uint64_t seq_)
786  {
787  std::ostringstream os;
788  os << seq_;
789  _message.setSequence(os.str());
790  return *this;
791  }
792  amps_uint64_t getSequence() const
793  {
794  return amps_message_get_field_uint64(_message.getMessage(), AMPS_Sequence);
795  }
798  Command& setData(const std::string& data_)
799  {
800  _message.setData(data_);
801  return *this;
802  }
806  Command& setData(const char* data_, size_t dataLen_)
807  {
808  _message.setData(data_, dataLen_);
809  return *this;
810  }
820  Command& setTimeout(unsigned timeout_)
821  {
822  _timeout = timeout_;
823  return *this;
824  }
826  Command& setTopN(unsigned topN_)
827  {
828  _message.setTopNRecordsReturned(topN_);
829  return *this;
830  }
835  Command& setBatchSize(unsigned batchSize_)
836  {
837  _message.setBatchSize(batchSize_);
838  _batchSize = batchSize_;
839  return *this;
840  }
851  Command& setExpiration(unsigned expiration_)
852  {
853  _message.setExpiration(expiration_);
854  return *this;
855  }
857  Command& addAckType(const std::string& ackType_)
858  {
859  _message.setAckType(_message.getAckType() + "," + ackType_);
860  if (ackType_ == "processed")
861  {
862  _flags |= ProcessedAck;
863  }
864  else if (ackType_ == "stats")
865  {
866  _flags |= StatsAck;
867  }
868  return *this;
869  }
871  Command& setAckType(const std::string& ackType_)
872  {
873  _message.setAckType(ackType_);
874  if (ackType_.find("processed") != std::string::npos)
875  {
876  _flags |= ProcessedAck;
877  }
878  else
879  {
880  _flags &= ~ProcessedAck;
881  }
882  if (ackType_.find("stats") != std::string::npos)
883  {
884  _flags |= StatsAck;
885  }
886  else
887  {
888  _flags &= ~StatsAck;
889  }
890  return *this;
891  }
893  Command& setAckType(unsigned ackType_)
894  {
895  _message.setAckTypeEnum(ackType_);
896  if (ackType_ & Message::AckType::Processed)
897  {
898  _flags |= ProcessedAck;
899  }
900  else
901  {
902  _flags &= ~ProcessedAck;
903  }
904  if (ackType_ & Message::AckType::Stats)
905  {
906  _flags |= StatsAck;
907  }
908  else
909  {
910  _flags &= ~StatsAck;
911  }
912  return *this;
913  }
915  std::string getAckType() const
916  {
917  return (std::string)(_message.getAckType());
918  }
920  unsigned getAckTypeEnum() const
921  {
922  return _message.getAckTypeEnum();
923  }
924 
925  Message& getMessage(void)
926  {
927  return _message;
928  }
929  unsigned getTimeout(void) const
930  {
931  return _timeout;
932  }
933  unsigned getBatchSize(void) const
934  {
935  return _batchSize;
936  }
937  bool isSubscribe(void) const
938  {
939  return _flags & Subscribe;
940  }
941  bool isSow(void) const
942  {
943  return (_flags & SOW) != 0;
944  }
945  bool hasProcessedAck(void) const
946  {
947  return (_flags & ProcessedAck) != 0;
948  }
949  bool hasStatsAck(void) const
950  {
951  return (_flags & StatsAck) != 0;
952  }
953  bool needsSequenceNumber(void) const
954  {
955  return (_flags & NeedsSequenceNumber) != 0;
956  }
957  };
958 
961  typedef void(*DisconnectHandlerFunc)(Client&, void* userData);
962 
963  class Message;
965 
969  {
970  public:
971  virtual ~Authenticator() {;}
972 
978  virtual std::string authenticate(const std::string& userName_, const std::string& password_) = 0;
986  virtual std::string retry(const std::string& userName_, const std::string& password_) = 0;
993  virtual void completed(const std::string& userName_, const std::string& password_, const std::string& reason_) = 0;
994  };
995 
1000  {
1001  public:
1002  virtual ~DefaultAuthenticator() {;}
1005  std::string authenticate(const std::string& /*userName_*/, const std::string& password_)
1006  {
1007  return password_;
1008  }
1009 
1012  std::string retry(const std::string& /*userName_*/, const std::string& /*password_*/)
1013  {
1014  throw AuthenticationException("retry not implemented by DefaultAuthenticator.");
1015  }
1016 
1017  void completed(const std::string& /*userName_*/, const std::string& /* password_ */, const std::string& /* reason */) {;}
1018 
1023  {
1024  static DefaultAuthenticator d;
1025  return d;
1026  }
1027  };
1028 
1032  {
1033  public:
1034 
1038  virtual void execute(Message& message_) = 0;
1039 
1040  virtual ~StoreReplayer() {;}
1041  };
1042 
1043  class Store;
1044 
1053  typedef bool (*PublishStoreResizeHandler)(Store store_,
1054  size_t size_,
1055  void* userData_);
1056 
1059  class StoreImpl : public RefBody
1060  {
1061  public:
1067  StoreImpl(bool errorOnPublishGap_ = false)
1068  : _resizeHandler(NULL)
1069  , _resizeHandlerData(NULL)
1070  , _errorOnPublishGap(errorOnPublishGap_)
1071  {;}
1072 
1077  virtual amps_uint64_t store(const Message& message_) = 0;
1078 
1085  virtual void discardUpTo(amps_uint64_t index_) = 0;
1086 
1091  virtual void replay(StoreReplayer& replayer_) = 0;
1092 
1100  virtual bool replaySingle(StoreReplayer& replayer_, amps_uint64_t index_) = 0;
1101 
1106  virtual size_t unpersistedCount() const = 0;
1107 
1108  virtual ~StoreImpl() {;}
1109 
1118  virtual void flush(long timeout_) = 0;
1119 
1122  static inline size_t getUnsetPosition()
1123  {
1124  return AMPS_UNSET_INDEX;
1125  }
1126 
1129  static inline amps_uint64_t getUnsetSequence()
1130  {
1131  return AMPS_UNSET_SEQUENCE;
1132  }
1133 
1137  virtual amps_uint64_t getLowestUnpersisted() const = 0;
1138 
1142  virtual amps_uint64_t getLastPersisted() = 0;
1143 
1153  inline virtual void setResizeHandler(PublishStoreResizeHandler handler_,
1154  void* userData_)
1155  {
1156  _resizeHandler = handler_;
1157  _resizeHandlerData = userData_;
1158  }
1159 
1160  inline virtual PublishStoreResizeHandler getResizeHandler() const
1161  {
1162  return _resizeHandler;
1163  }
1164 
1165  bool callResizeHandler(size_t newSize_);
1166 
1167  inline virtual void setErrorOnPublishGap(bool errorOnPublishGap_)
1168  {
1169  _errorOnPublishGap = errorOnPublishGap_;
1170  }
1171 
1172  inline virtual bool getErrorOnPublishGap() const
1173  {
1174  return _errorOnPublishGap;
1175  }
1176 
1177  private:
1178  PublishStoreResizeHandler _resizeHandler;
1179  void* _resizeHandlerData;
1180  bool _errorOnPublishGap;
1181  };
1182 
1185  class Store
1186  {
1187  RefHandle<StoreImpl> _body;
1188  public:
1189  Store() {;}
1190  Store(StoreImpl* body_) : _body(body_) {;}
1191  Store(const Store& rhs) : _body(rhs._body) {;}
1192  Store& operator=(const Store& rhs)
1193  {
1194  _body = rhs._body;
1195  return *this;
1196  }
1197 
1201  amps_uint64_t store(const Message& message_)
1202  {
1203  return _body.get().store(message_);
1204  }
1205 
1212  void discardUpTo(amps_uint64_t index_)
1213  {
1214  _body.get().discardUpTo(index_);
1215  }
1216 
1221  void replay(StoreReplayer& replayer_)
1222  {
1223  _body.get().replay(replayer_);
1224  }
1225 
1233  bool replaySingle(StoreReplayer& replayer_, amps_uint64_t index_)
1234  {
1235  return _body.get().replaySingle(replayer_, index_);
1236  }
1237 
1242  size_t unpersistedCount() const
1243  {
1244  return _body.get().unpersistedCount();
1245  }
1246 
1250  bool isValid() const
1251  {
1252  return _body.isValid();
1253  }
1254 
1263  void flush(long timeout_ = 0)
1264  {
1265  return _body.get().flush(timeout_);
1266  }
1267 
1271  amps_uint64_t getLowestUnpersisted()
1272  {
1273  return _body.get().getLowestUnpersisted();
1274  }
1275 
1279  amps_uint64_t getLastPersisted()
1280  {
1281  return _body.get().getLastPersisted();
1282  }
1283 
1293  void setResizeHandler(PublishStoreResizeHandler handler_,
1294  void* userData_)
1295  {
1296  _body.get().setResizeHandler(handler_, userData_);
1297  }
1298 
1299  PublishStoreResizeHandler getResizeHandler() const
1300  {
1301  return _body.get().getResizeHandler();
1302  }
1303 
1308  inline void setErrorOnPublishGap(bool errorOnPublishGap_)
1309  {
1310  _body.get().setErrorOnPublishGap(errorOnPublishGap_);
1311  }
1312 
1317  inline bool getErrorOnPublishGap() const
1318  {
1319  return _body.get().getErrorOnPublishGap();
1320  }
1321 
1325  StoreImpl* get()
1326  {
1327  if (_body.isValid())
1328  {
1329  return &_body.get();
1330  }
1331  else
1332  {
1333  return NULL;
1334  }
1335  }
1336 
1337  };
1338 
1344  {
1345  public:
1346  virtual ~FailedWriteHandler() {;}
1353  virtual void failedWrite(const Message& message_,
1354  const char* reason_, size_t reasonLength_) = 0;
1355  };
1356 
1357 
1358  inline bool StoreImpl::callResizeHandler(size_t newSize_)
1359  {
1360  if (_resizeHandler)
1361  {
1362  return _resizeHandler(Store(this), newSize_, _resizeHandlerData);
1363  }
1364  return true;
1365  }
1366 
1373  inline bool DangerousFlushPublishStoreResizeHandler(Store store_, size_t /*size_*/,
1374  void* data_)
1375  {
1376  long* timeoutp = (long*)data_;
1377  size_t count = store_.unpersistedCount();
1378  if (count == 0)
1379  {
1380  return false;
1381  }
1382  try
1383  {
1384  store_.flush(*timeoutp);
1385  }
1386 #ifdef _WIN32
1387  catch (const TimedOutException&)
1388 #else
1389  catch (const TimedOutException& e)
1390 #endif
1391  {
1392  return true;
1393  }
1394  return (count == store_.unpersistedCount());
1395  }
1396 
1402 {
1403 public:
1415  virtual bool failure(const Message& message_, const MessageHandler& handler_,
1416  unsigned requestedAckTypes_,
1417  const AMPSException& exception_) = 0;
1418 };
1419 
1424  {
1425  public:
1426  virtual ~SubscriptionManager() {;}
1434  virtual void subscribe(MessageHandler messageHandler_, const Message& message_,
1435  unsigned requestedAckTypes_) = 0;
1439  virtual void unsubscribe(const Message::Field& subId_) = 0;
1442  virtual void clear() = 0;
1446  virtual void resubscribe(Client& client_) = 0;
1451  virtual void setFailedResubscribeHandler(std::shared_ptr<FailedResubscribeHandler> handler_)
1452  {
1453  _failedResubscribeHandler = handler_;
1454  }
1455  protected:
1456  std::shared_ptr<FailedResubscribeHandler> _failedResubscribeHandler;
1457  };
1458 
1462 
1464  {
1465  public:
1467  typedef enum { Disconnected = 0,
1468  Shutdown = 1,
1469  Connected = 2,
1470  LoggedOn = 4,
1471  PublishReplayed = 8,
1472  HeartbeatInitiated = 16,
1473  Resubscribed = 32,
1474  UNKNOWN = 16384
1475  } State;
1476 
1486  virtual void connectionStateChanged(State newState_) = 0;
1487  virtual ~ConnectionStateListener() {;};
1488  };
1489 
1490 
1491  class MessageStreamImpl;
1492  class MessageStream;
1493 
1494  typedef void(*DeferredExecutionFunc)(void*);
1495 
1496  class ClientImpl : public RefBody // -V553
1497  {
1498  // Class to wrap turning of Nagle for things like flush and logon
1499  class NoDelay
1500  {
1501  private:
1502  AMPS_SOCKET _socket;
1503  int _noDelay;
1504  char* _valuePtr;
1505 #ifdef _WIN32
1506  int _valueLen;
1507 #else
1508  socklen_t _valueLen;
1509 #endif
1510  public:
1511  NoDelay(amps_handle client_)
1512  : _socket(AMPS_INVALID_SOCKET), _noDelay(0), _valueLen(sizeof(int))
1513  {
1514  _valuePtr = (char*)&_noDelay;
1515  _socket = amps_client_get_socket(client_);
1516  if (_socket != AMPS_INVALID_SOCKET)
1517  {
1518  getsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, _valuePtr, &_valueLen);
1519  if (!_noDelay)
1520  {
1521  _noDelay = 1;
1522  setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, _valuePtr, _valueLen);
1523  }
1524  else
1525  {
1526  _socket = AMPS_INVALID_SOCKET;
1527  }
1528  }
1529  }
1530 
1531  ~NoDelay()
1532  {
1533  if (_socket != AMPS_INVALID_SOCKET)
1534  {
1535  _noDelay = 0;
1536  setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, _valuePtr, _valueLen);
1537  }
1538  }
1539  };
1540 
1541  friend class Client;
1542  protected:
1543  amps_handle _client;
1544  DisconnectHandler _disconnectHandler;
1545  enum GlobalCommandTypeHandlers : size_t
1546  {
1547  Publish = 0,
1548  SOW = 1,
1549  GroupBegin = 2,
1550  GroupEnd = 3,
1551  Heartbeat = 4,
1552  OOF = 5,
1553  Ack = 6,
1554  LastChance = 7,
1555  DuplicateMessage = 8,
1556  COUNT = 9
1557  };
1558  std::vector<MessageHandler> _globalCommandTypeHandlers;
1559  Message _message, _readMessage, _publishMessage, _deltaMessage, _beatMessage;
1560  MessageRouter _routes;
1561  MessageRouter::RouteCache _routeCache;
1562  mutable Mutex _lock;
1563  std::string _name, _nameHash, _lastUri, _logonCorrelationData;
1564  amps_uint64_t _nameHashValue;
1565  BookmarkStore _bookmarkStore;
1566  Store _publishStore;
1567  bool _isRetryOnDisconnect;
1568  amps_unique_ptr<FailedWriteHandler> _failedWriteHandler;
1569 #if __cplusplus >= 201100L || _MSC_VER >= 1900
1570  std::atomic<amps_uint64_t> _lastSentHaSequenceNumber;
1571 #else
1572  volatile amps_uint64_t _lastSentHaSequenceNumber;
1573 #endif
1574  AMPS_ATOMIC_TYPE_8 _badTimeToHAPublish;
1575  AMPS_ATOMIC_TYPE_8 _badTimeToHASubscribe;
1576  VersionInfo _serverVersion;
1577  Timer _heartbeatTimer;
1578  amps_unique_ptr<MessageStream> _pEmptyMessageStream;
1579 
1580  // queue data
1581  int _queueAckTimeout;
1582  bool _isAutoAckEnabled;
1583  unsigned _ackBatchSize;
1584  unsigned _queuedAckCount;
1585  unsigned _defaultMaxDepth;
1586  struct QueueBookmarks
1587  {
1588  QueueBookmarks(const std::string& topic_)
1589  : _topic(topic_)
1590  , _oldestTime(0)
1591  , _bookmarkCount(0)
1592  {;}
1593  std::string _topic;
1594  std::string _data;
1595  amps_uint64_t _oldestTime;
1596  unsigned _bookmarkCount;
1597  };
1598  typedef amps_uint64_t topic_hash;
1599  typedef std::map<topic_hash, QueueBookmarks> TopicHashMap;
1600  TopicHashMap _topicHashMap;
1601 
1602  class ClientStoreReplayer : public StoreReplayer
1603  {
1604  ClientImpl* _client;
1605  public:
1606  unsigned _version;
1607  amps_result _res;
1608 
1609  ClientStoreReplayer()
1610  : _client(NULL), _version(0), _res(AMPS_E_OK)
1611  {}
1612 
1613  ClientStoreReplayer(ClientImpl* client_)
1614  : _client(client_), _version(0), _res(AMPS_E_OK)
1615  {}
1616 
1617  void setClient(ClientImpl* client_)
1618  {
1619  _client = client_;
1620  }
1621 
1622  void execute(Message& message_)
1623  {
1624  if (!_client)
1625  {
1626  throw CommandException("Can't replay without a client.");
1627  }
1628  amps_uint64_t index = amps_message_get_field_uint64(message_.getMessage(),
1629  AMPS_Sequence);
1630  if (index > _client->_lastSentHaSequenceNumber)
1631  {
1632  _client->_lastSentHaSequenceNumber = index;
1633  }
1634 
1635  _res = AMPS_E_OK;
1636  // Don't replay a queue cancel message after a reconnect.
1637  // Currently, the only messages that will have anything in options
1638  // are cancel messages.
1639  if (!message_.getCommand().empty() &&
1640  (!_client->_badTimeToHAPublish ||
1641  message_.getOptions().len() < 6))
1642  {
1643  _res = amps_client_send_with_version(_client->_client,
1644  message_.getMessage(),
1645  &_version);
1646  if (_res != AMPS_E_OK)
1647  {
1648  throw DisconnectedException("AMPS Server disconnected during replay");
1649  }
1650  }
1651  }
1652 
1653  };
1654  ClientStoreReplayer _replayer;
1655 
1656  class FailedWriteStoreReplayer : public StoreReplayer
1657  {
1658  ClientImpl* _parent;
1659  const char* _reason;
1660  size_t _reasonLength;
1661  size_t _replayCount;
1662  public:
1663  FailedWriteStoreReplayer(ClientImpl* parent, const char* reason_, size_t reasonLength_)
1664  : _parent(parent),
1665  _reason(reason_),
1666  _reasonLength(reasonLength_),
1667  _replayCount(0)
1668  {;}
1669  void execute(Message& message_)
1670  {
1671  if (_parent->_failedWriteHandler)
1672  {
1673  ++_replayCount;
1674  _parent->_failedWriteHandler->failedWrite(message_,
1675  _reason, _reasonLength);
1676  }
1677  }
1678  size_t replayCount(void) const
1679  {
1680  return _replayCount;
1681  }
1682  };
1683 
1684  struct AckResponseImpl : public RefBody
1685  {
1686  std::string username, password, reason, status, bookmark, options;
1687  amps_uint64_t sequenceNo;
1688  amps_uint64_t nameHashValue;
1689  VersionInfo serverVersion;
1690 #if __cplusplus >= 201100L || _MSC_VER >= 1900
1691  std::atomic<bool> responded;
1692  std::atomic<bool> abandoned;
1693 #else
1694  volatile bool responded;
1695  volatile bool abandoned;
1696 #endif
1697  unsigned connectionVersion;
1698  AckResponseImpl() :
1699  RefBody(),
1700  sequenceNo((amps_uint64_t)0),
1701  serverVersion(),
1702  responded(false),
1703  abandoned(false),
1704  connectionVersion(0)
1705  {
1706  }
1707  };
1708 
1709  class AckResponse
1710  {
1711  RefHandle<AckResponseImpl> _body;
1712  public:
1713  AckResponse() : _body(NULL) {;}
1714  AckResponse(const AckResponse& rhs) : _body(rhs._body) {;}
1715  static AckResponse create()
1716  {
1717  AckResponse r;
1718  r._body = new AckResponseImpl();
1719  return r;
1720  }
1721 
1722  const std::string& username()
1723  {
1724  return _body.get().username;
1725  }
1726  void setUsername(const char* data_, size_t len_)
1727  {
1728  if (data_)
1729  {
1730  _body.get().username.assign(data_, len_);
1731  }
1732  else
1733  {
1734  _body.get().username.clear();
1735  }
1736  }
1737  const std::string& password()
1738  {
1739  return _body.get().password;
1740  }
1741  void setPassword(const char* data_, size_t len_)
1742  {
1743  if (data_)
1744  {
1745  _body.get().password.assign(data_, len_);
1746  }
1747  else
1748  {
1749  _body.get().password.clear();
1750  }
1751  }
1752  const std::string& reason()
1753  {
1754  return _body.get().reason;
1755  }
1756  void setReason(const char* data_, size_t len_)
1757  {
1758  if (data_)
1759  {
1760  _body.get().reason.assign(data_, len_);
1761  }
1762  else
1763  {
1764  _body.get().reason.clear();
1765  }
1766  }
1767  const std::string& status()
1768  {
1769  return _body.get().status;
1770  }
1771  void setStatus(const char* data_, size_t len_)
1772  {
1773  if (data_)
1774  {
1775  _body.get().status.assign(data_, len_);
1776  }
1777  else
1778  {
1779  _body.get().status.clear();
1780  }
1781  }
1782  const std::string& bookmark()
1783  {
1784  return _body.get().bookmark;
1785  }
1786  void setBookmark(const Field& bookmark_)
1787  {
1788  if (!bookmark_.empty())
1789  {
1790  _body.get().bookmark.assign(bookmark_.data(), bookmark_.len());
1791  Field::parseBookmark(bookmark_, _body.get().nameHashValue,
1792  _body.get().sequenceNo);
1793  }
1794  else
1795  {
1796  _body.get().bookmark.clear();
1797  _body.get().sequenceNo = (amps_uint64_t)0;
1798  _body.get().nameHashValue = (amps_uint64_t)0;
1799  }
1800  }
1801  amps_uint64_t sequenceNo() const
1802  {
1803  return _body.get().sequenceNo;
1804  }
1805  amps_uint64_t nameHashValue() const
1806  {
1807  return _body.get().nameHashValue;
1808  }
1809  void setSequenceNo(const char* data_, size_t len_)
1810  {
1811  amps_uint64_t result = (amps_uint64_t)0;
1812  if (data_)
1813  {
1814  for (size_t i = 0; i < len_; ++i)
1815  {
1816  result *= (amps_uint64_t)10;
1817  result += (amps_uint64_t)(data_[i] - '0');
1818  }
1819  }
1820  _body.get().sequenceNo = result;
1821  }
1822  VersionInfo serverVersion() const
1823  {
1824  return _body.get().serverVersion;
1825  }
1826  void setServerVersion(const char* data_, size_t len_)
1827  {
1828  if (data_)
1829  {
1830  _body.get().serverVersion.setVersion(std::string(data_, len_));
1831  }
1832  }
1833  bool responded()
1834  {
1835  return _body.get().responded;
1836  }
1837  void setResponded()
1838  {
1839  _body.get().responded = true;
1840  }
1841  bool abandoned()
1842  {
1843  return _body.get().abandoned;
1844  }
1845  void setAbandoned()
1846  {
1847  if (_body.isValid())
1848  {
1849  _body.get().abandoned = true;
1850  }
1851  }
1852 
1853  void setConnectionVersion(unsigned connectionVersion)
1854  {
1855  _body.get().connectionVersion = connectionVersion;
1856  }
1857 
1858  unsigned getConnectionVersion()
1859  {
1860  return _body.get().connectionVersion;
1861  }
1862  void setOptions(const char* data_, size_t len_)
1863  {
1864  if (data_)
1865  {
1866  _body.get().options.assign(data_, len_);
1867  }
1868  else
1869  {
1870  _body.get().options.clear();
1871  }
1872  }
1873 
1874  const std::string& options()
1875  {
1876  return _body.get().options;
1877  }
1878 
1879  AckResponse& operator=(const AckResponse& rhs)
1880  {
1881  _body = rhs._body;
1882  return *this;
1883  }
1884  };
1885 
1886 
1887  typedef std::map<std::string, AckResponse> AckMap;
1888  AckMap _ackMap;
1889  Mutex _ackMapLock;
1890  DefaultExceptionListener _defaultExceptionListener;
1891  protected:
1892 
1893  struct DeferredExecutionRequest
1894  {
1895  DeferredExecutionRequest(DeferredExecutionFunc func_,
1896  void* userData_)
1897  : _func(func_),
1898  _userData(userData_)
1899  {;}
1900 
1901  DeferredExecutionFunc _func;
1902  void* _userData;
1903  };
1904  const ExceptionListener* _exceptionListener;
1905  std::shared_ptr<const ExceptionListener> _pExceptionListener;
1906  amps_unique_ptr<SubscriptionManager> _subscriptionManager;
1907  volatile bool _connected;
1908  std::string _username;
1909  typedef std::set<ConnectionStateListener*> ConnectionStateListeners;
1910  ConnectionStateListeners _connectionStateListeners;
1911  typedef std::vector<DeferredExecutionRequest> DeferredExecutionList;
1912  Mutex _deferredExecutionLock;
1913  DeferredExecutionList _deferredExecutionList;
1914  unsigned _heartbeatInterval;
1915  unsigned _readTimeout;
1916 
1917  void broadcastConnectionStateChanged(ConnectionStateListener::State newState_)
1918  {
1919  // If we disconnected before we got to notification, don't notify.
1920  // This should only be able to happen for Resubscribed, since the lock
1921  // is released to let the subscription manager run resubscribe so a
1922  // disconnect could be called before the change is broadcast.
1923  if (!_connected && newState_ > ConnectionStateListener::Connected)
1924  {
1925  return;
1926  }
1927  for (ConnectionStateListeners::iterator it = _connectionStateListeners.begin(); it != _connectionStateListeners.end(); ++it)
1928  {
1929  AMPS_CALL_EXCEPTION_WRAPPER(
1930  (*it)->connectionStateChanged(newState_));
1931  }
1932  }
1933  unsigned processedAck(Message& message);
1934  unsigned persistedAck(Message& meesage);
1935  void lastChance(Message& message);
1936  void checkAndSendHeartbeat(bool force = false);
1937  virtual ConnectionInfo getConnectionInfo() const;
1938  static amps_result
1939  ClientImplMessageHandler(amps_handle message, void* userData);
1940  static void
1941  ClientImplPreDisconnectHandler(amps_handle client, unsigned failedConnectionVersion, void* userData);
1942  static amps_result
1943  ClientImplDisconnectHandler(amps_handle client, void* userData);
1944 
1945  void unsubscribeInternal(const std::string& id)
1946  {
1947  if (id.empty())
1948  {
1949  return;
1950  }
1951  // remove the handler first to avoid any more message delivery
1952  Message::Field subId;
1953  subId.assign(id.data(), id.length());
1954  _routes.removeRoute(subId);
1955  // Lock is already acquired
1956  if (_subscriptionManager)
1957  {
1958  // Have to unlock before calling into sub manager to avoid deadlock
1959  Unlock<Mutex> unlock(_lock);
1960  _subscriptionManager->unsubscribe(subId);
1961  }
1962  _message.reset();
1963  _message.setCommandEnum(Message::Command::Unsubscribe);
1964  _message.newCommandId();
1965  _message.setSubscriptionId(id);
1966  _sendWithoutRetry(_message);
1967  deferredExecution(&amps_noOpFn, NULL);
1968  }
1969 
1970  AckResponse syncAckProcessing(long timeout_, Message& message_,
1971  bool isHASubscribe_)
1972  {
1973  return syncAckProcessing(timeout_, message_,
1974  (amps_uint64_t)0, isHASubscribe_);
1975  }
1976 
1977  AckResponse syncAckProcessing(long timeout_, Message& message_,
1978  amps_uint64_t haSeq = (amps_uint64_t)0,
1979  bool isHASubscribe_ = false)
1980  {
1981  // inv: we already have _lock locked up.
1982  AckResponse ack = AckResponse::create();
1983  if (1)
1984  {
1985  Lock<Mutex> guard(_ackMapLock);
1986  _ackMap[message_.getCommandId()] = ack;
1987  }
1988  ack.setConnectionVersion((unsigned)_send(message_, haSeq, isHASubscribe_));
1989  if (ack.getConnectionVersion() == 0)
1990  {
1991  // Send failed
1992  throw DisconnectedException("Connection closed while waiting for response.");
1993  }
1994  bool timedOut = false;
1995  AMPS_START_TIMER(timeout_)
1996  while (!timedOut && !ack.responded() && !ack.abandoned() && _connected)
1997  {
1998  if (timeout_)
1999  {
2000  timedOut = !_lock.wait(timeout_);
2001  // May have woken up early, check real time
2002  if (timedOut)
2003  {
2004  AMPS_RESET_TIMER(timedOut, timeout_);
2005  }
2006  }
2007  else
2008  {
2009  // Using a timeout version to ensure python can interrupt
2010  _lock.wait(1000);
2011  Unlock<Mutex> unlck(_lock);
2012  amps_invoke_waiting_function();
2013  }
2014  }
2015  if (ack.responded())
2016  {
2017  if (ack.status() != "failure")
2018  {
2019  if (message_.getCommand() == "logon")
2020  {
2021  amps_uint64_t ackSequence = ack.sequenceNo();
2022  if (_lastSentHaSequenceNumber < ackSequence)
2023  {
2024  _lastSentHaSequenceNumber = ackSequence;
2025  }
2026  if (_publishStore.isValid())
2027  {
2028  // If this throws, logon will fail and eitehr be
2029  // handled in HAClient/ServerChooser or by the caller
2030  // of logon.
2031  _publishStore.discardUpTo(ackSequence);
2032  if (_lastSentHaSequenceNumber < _publishStore.getLastPersisted())
2033  {
2034  _lastSentHaSequenceNumber = _publishStore.getLastPersisted();
2035  }
2036  }
2037  _nameHash = ack.bookmark().substr(0, ack.bookmark().find('|'));
2038  _nameHashValue = ack.nameHashValue();
2039  _serverVersion = ack.serverVersion();
2040  if (_bookmarkStore.isValid())
2041  {
2042  _bookmarkStore.setServerVersion(_serverVersion);
2043  }
2044  }
2045  if (_ackBatchSize)
2046  {
2047  const std::string& options = ack.options();
2048  size_t index = options.find_first_of("max_backlog=");
2049  if (index != std::string::npos)
2050  {
2051  unsigned data = 0;
2052  const char* c = options.c_str() + index + 12;
2053  while (*c && *c != ',')
2054  {
2055  data = (data * 10) + (unsigned)(*c++ -48);
2056  }
2057  if (_ackBatchSize > data)
2058  {
2059  _ackBatchSize = data;
2060  }
2061  }
2062  }
2063  return ack;
2064  }
2065  const size_t NotEntitled = 12;
2066  std::string ackReason = ack.reason();
2067  if (ackReason.length() == 0)
2068  {
2069  return ack; // none
2070  }
2071  if (ackReason.length() == NotEntitled &&
2072  ackReason[0] == 'n' &&
2073  message_.getUserId().len() == 0)
2074  {
2075  message_.assignUserId(_username);
2076  }
2077  message_.throwFor(_client, ackReason);
2078  }
2079  else // !ack.responded()
2080  {
2081  if (!ack.abandoned())
2082  {
2083  throw TimedOutException("timed out waiting for operation.");
2084  }
2085  else
2086  {
2087  throw DisconnectedException("Connection closed while waiting for response.");
2088  }
2089  }
2090  return ack;
2091  }
2092 
2093  void _cleanup(void)
2094  {
2095  if (!_client)
2096  {
2097  return;
2098  }
2099  amps_client_set_predisconnect_handler(_client, NULL, 0L);
2100  amps_client_set_disconnect_handler(_client, NULL, 0L);
2101  AMPS_CALL_EXCEPTION_WRAPPER(ClientImpl::disconnect());
2102  _pEmptyMessageStream.reset(NULL);
2103  amps_client_destroy(_client);
2104  _client = NULL;
2105  }
2106 
2107  public:
2108 
2109  ClientImpl(const std::string& clientName)
2110  : _client(NULL), _name(clientName)
2111  , _isRetryOnDisconnect(true)
2112  , _lastSentHaSequenceNumber((amps_uint64_t)0), _badTimeToHAPublish(0)
2113  , _badTimeToHASubscribe(0), _serverVersion()
2114  , _queueAckTimeout(AMPS_DEFAULT_QUEUE_ACK_TIMEOUT)
2115  , _isAutoAckEnabled(false)
2116  , _ackBatchSize(0)
2117  , _queuedAckCount(0)
2118  , _defaultMaxDepth(0)
2119  , _connected(false)
2120  , _heartbeatInterval(0)
2121  , _readTimeout(0)
2122  {
2123  _replayer.setClient(this);
2124  _client = amps_client_create(clientName.c_str());
2126  (amps_handler)ClientImpl::ClientImplMessageHandler,
2127  this);
2129  (amps_predisconnect_handler)ClientImpl::ClientImplPreDisconnectHandler,
2130  this);
2132  (amps_handler)ClientImpl::ClientImplDisconnectHandler,
2133  this);
2134  _exceptionListener = &_defaultExceptionListener;
2135  for (size_t i = 0; i < GlobalCommandTypeHandlers::COUNT; ++i)
2136  {
2137 #ifdef AMPS_USE_EMPLACE
2138  _globalCommandTypeHandlers.emplace_back(MessageHandler());
2139 #else
2140  _globalCommandTypeHandlers.push_back(MessageHandler());
2141 #endif
2142  }
2143  }
2144 
2145  virtual ~ClientImpl()
2146  {
2147  _cleanup();
2148  }
2149 
2150  const std::string& getName() const
2151  {
2152  return _name;
2153  }
2154 
2155  const std::string& getNameHash() const
2156  {
2157  return _nameHash;
2158  }
2159 
2160  const amps_uint64_t getNameHashValue() const
2161  {
2162  return _nameHashValue;
2163  }
2164 
2165  void setName(const std::string& name)
2166  {
2167  // This operation will fail if the client's
2168  // name is already set.
2169  amps_result result = amps_client_set_name(_client, name.c_str());
2170  if (result != AMPS_E_OK)
2171  {
2172  AMPSException::throwFor(_client, result);
2173  }
2174  _name = name;
2175  }
2176 
2177  const std::string& getLogonCorrelationData() const
2178  {
2179  return _logonCorrelationData;
2180  }
2181 
2182  void setLogonCorrelationData(const std::string& logonCorrelationData_)
2183  {
2184  _logonCorrelationData = logonCorrelationData_;
2185  }
2186 
2187  size_t getServerVersion() const
2188  {
2189  return _serverVersion.getOldStyleVersion();
2190  }
2191 
2192  VersionInfo getServerVersionInfo() const
2193  {
2194  return _serverVersion;
2195  }
2196 
2197  const std::string& getURI() const
2198  {
2199  return _lastUri;
2200  }
2201 
2202  virtual void connect(const std::string& uri)
2203  {
2204  Lock<Mutex> l(_lock);
2205  _connect(uri);
2206  }
2207 
2208  virtual void _connect(const std::string& uri)
2209  {
2210  _lastUri = uri;
2211  amps_result result = amps_client_connect(_client, uri.c_str());
2212  if (result != AMPS_E_OK)
2213  {
2214  AMPSException::throwFor(_client, result);
2215  }
2216  _message.reset();
2217  _deltaMessage.setCommandEnum(Message::Command::DeltaPublish);
2218  _publishMessage.setCommandEnum(Message::Command::Publish);
2219  _beatMessage.setCommandEnum(Message::Command::Heartbeat);
2220  _beatMessage.setOptions("beat");
2221  _readMessage.setClientImpl(this);
2222  if (_queueAckTimeout)
2223  {
2224  result = amps_client_set_idle_time(_client, _queueAckTimeout);
2225  if (result != AMPS_E_OK)
2226  {
2227  AMPSException::throwFor(_client, result);
2228  }
2229  }
2230  _connected = true;
2231  broadcastConnectionStateChanged(ConnectionStateListener::Connected);
2232  }
2233 
2234  void setDisconnected()
2235  {
2236  {
2237  Lock<Mutex> l(_lock);
2238  if (_connected)
2239  {
2240  AMPS_CALL_EXCEPTION_WRAPPER(broadcastConnectionStateChanged(ConnectionStateListener::Disconnected));
2241  }
2242  _connected = false;
2243  _heartbeatTimer.setTimeout(0.0);
2244  }
2245  clearAcks(INT_MAX);
2246  amps_client_disconnect(_client);
2247  _routes.clear();
2248  }
2249 
2250  virtual void disconnect()
2251  {
2252  AMPS_CALL_EXCEPTION_WRAPPER(flushAcks());
2253  setDisconnected();
2254  AMPS_CALL_EXCEPTION_WRAPPER(processDeferredExecutions());
2255  Lock<Mutex> l(_lock);
2256  broadcastConnectionStateChanged(ConnectionStateListener::Shutdown);
2257  }
2258 
2259  void clearAcks(unsigned failedVersion)
2260  {
2261  // Have to lock to prevent race conditions
2262  Lock<Mutex> guard(_ackMapLock);
2263  {
2264  // Go ahead and signal any waiters if they are around...
2265  std::vector<std::string> worklist;
2266  for (AckMap::iterator i = _ackMap.begin(), e = _ackMap.end(); i != e; ++i)
2267  {
2268  if (i->second.getConnectionVersion() <= failedVersion)
2269  {
2270  i->second.setAbandoned();
2271  worklist.push_back(i->first);
2272  }
2273  }
2274 
2275  for (std::vector<std::string>::iterator j = worklist.begin(), e = worklist.end(); j != e; ++j)
2276  {
2277  _ackMap.erase(*j);
2278  }
2279  }
2280 
2281  _lock.signalAll();
2282  }
2283 
2284  int send(const Message& message)
2285  {
2286  Lock<Mutex> l(_lock);
2287  return _send(message);
2288  }
2289 
2290  void sendWithoutRetry(const Message& message_)
2291  {
2292  Lock<Mutex> l(_lock);
2293  _sendWithoutRetry(message_);
2294  }
2295 
2296  void _sendWithoutRetry(const Message& message_)
2297  {
2298  amps_result result = amps_client_send(_client, message_.getMessage());
2299  if (result != AMPS_E_OK)
2300  {
2301  AMPSException::throwFor(_client, result);
2302  }
2303  }
2304 
2305  int _send(const Message& message, amps_uint64_t haSeq = (amps_uint64_t)0,
2306  bool isHASubscribe_ = false)
2307  {
2308  // Lock is already acquired
2309  amps_result result = AMPS_E_RETRY;
2310 
2311  // Create a local reference to this message, as we'll need to hold on
2312  // to a reference to it in case reconnect occurs.
2313  Message localMessage = message;
2314  unsigned version = 0;
2315 
2316  while (result == AMPS_E_RETRY)
2317  {
2318  if (haSeq != (amps_uint64_t)0 && _badTimeToHAPublish > 0)
2319  {
2320  // If retrySend is disabled, do not wait for the reconnect
2321  // to finish, just throw.
2322  if (!_isRetryOnDisconnect)
2323  {
2324  AMPSException::throwFor(_client, AMPS_E_RETRY);
2325  }
2326  if (!_lock.wait(1000))
2327  {
2328  amps_invoke_waiting_function();
2329  }
2330  }
2331  else
2332  {
2333  if ((haSeq && haSeq <= _lastSentHaSequenceNumber) ||
2334  (isHASubscribe_ && _badTimeToHASubscribe != 0))
2335  {
2336  return (int)version;
2337  }
2338  // It's possible to get here out of order, but this way we'll
2339  // always send in order.
2340  if (haSeq > _lastSentHaSequenceNumber)
2341  {
2342  while (haSeq > _lastSentHaSequenceNumber + 1)
2343  {
2344  try
2345  {
2346  // Replayer updates _lastSentHaSsequenceNumber
2347  if (!_publishStore.replaySingle(_replayer,
2348  _lastSentHaSequenceNumber + 1))
2349  {
2350  //++_lastSentHaSequenceNumber;
2351  continue;
2352  }
2353  result = AMPS_E_OK;
2354  version = _replayer._version;
2355  }
2356 #ifdef _WIN32
2357  catch (const DisconnectedException&)
2358 #else
2359  catch (const DisconnectedException& e)
2360 #endif
2361  {
2362  result = _replayer._res;
2363  break;
2364  }
2365  }
2366  result = amps_client_send_with_version(_client,
2367  localMessage.getMessage(),
2368  &version);
2369  ++_lastSentHaSequenceNumber;
2370  }
2371  else
2372  result = amps_client_send_with_version(_client,
2373  localMessage.getMessage(),
2374  &version);
2375  if (result != AMPS_E_OK)
2376  {
2377  if (!isHASubscribe_ && !haSeq &&
2378  localMessage.getMessage() == message.getMessage())
2379  {
2380  localMessage = message.deepCopy();
2381  }
2382  if (_isRetryOnDisconnect)
2383  {
2384  Unlock<Mutex> u(_lock);
2385  result = amps_client_attempt_reconnect(_client, version);
2386  // If this is an HA publish or subscribe command, it was
2387  // stored first and will have already been replayed by the
2388  // store or sub manager after reconnect, so just return.
2389  if ((isHASubscribe_ || haSeq) &&
2390  result == AMPS_E_RETRY)
2391  {
2392  return (int)version;
2393  }
2394  }
2395  else
2396  {
2397  // retrySend is disabled so throw the error
2398  // from the send as an exception, do not retry.
2399  AMPSException::throwFor(_client, result);
2400  }
2401  }
2402  }
2403  if (result == AMPS_E_RETRY)
2404  {
2405  amps_invoke_waiting_function();
2406  }
2407  }
2408 
2409  if (result != AMPS_E_OK)
2410  {
2411  AMPSException::throwFor(_client, result);
2412  }
2413  return (int)version;
2414  }
2415 
2416  void addMessageHandler(const Field& commandId_,
2417  const AMPS::MessageHandler& messageHandler_,
2418  unsigned requestedAcks_, bool isSubscribe_)
2419  {
2420  Lock<Mutex> lock(_lock);
2421  _routes.addRoute(commandId_, messageHandler_, requestedAcks_,
2422  0, isSubscribe_);
2423  }
2424 
2425  bool removeMessageHandler(const Field& commandId_)
2426  {
2427  Lock<Mutex> lock(_lock);
2428  return _routes.removeRoute(commandId_);
2429  }
2430 
2431  std::string send(const MessageHandler& messageHandler_, Message& message_, int timeout_ = 0)
2432  {
2433  Field id = message_.getCommandId();
2434  Field subId = message_.getSubscriptionId();
2435  Field qid = message_.getQueryId();
2436  bool isSubscribe = false;
2437  bool isSubscribeOnly = false;
2438  bool replace = false;
2439  unsigned requestedAcks = message_.getAckTypeEnum();
2440  unsigned systemAddedAcks = Message::AckType::None;
2441 
2442  switch (message_.getCommandEnum())
2443  {
2444  case Message::Command::Subscribe:
2445  case Message::Command::DeltaSubscribe:
2446  replace = message_.getOptions().operator std::string().find(AMPS_OPTIONS_REPLACE, 0, strlen(AMPS_OPTIONS_REPLACE) - 1) != std::string::npos;
2447  isSubscribeOnly = true;
2448  // fall through
2449  case Message::Command::SOWAndSubscribe:
2450  case Message::Command::SOWAndDeltaSubscribe:
2451  if (id.empty())
2452  {
2453  id = message_.newCommandId().getCommandId();
2454  }
2455  else
2456  {
2457  while (!replace && id != subId && _routes.hasRoute(id))
2458  {
2459  id = message_.newCommandId().getCommandId();
2460  }
2461  }
2462  if (subId.empty())
2463  {
2464  message_.setSubscriptionId(id);
2465  subId = id;
2466  }
2467  isSubscribe = true;
2468  if (!message_.getBookmark().empty() && _bookmarkStore.isValid())
2469  {
2470  systemAddedAcks |= Message::AckType::Persisted;
2471  }
2472  // fall through
2473  case Message::Command::SOW:
2474  if (id.empty())
2475  {
2476  id = message_.newCommandId().getCommandId();
2477  }
2478  else
2479  {
2480  while (!replace && id != subId && _routes.hasRoute(id))
2481  {
2482  message_.newCommandId();
2483  if (qid == id)
2484  {
2485  qid = message_.getCommandId();
2486  message_.setQueryId(qid);
2487  }
2488  id = message_.getCommandId();
2489  }
2490  }
2491  if (!isSubscribeOnly)
2492  {
2493  if (qid.empty())
2494  {
2495  message_.setQueryID(id);
2496  qid = id;
2497  }
2498  else
2499  {
2500  while (!replace && qid != subId && qid != id
2501  && _routes.hasRoute(qid))
2502  {
2503  qid = message_.newQueryId().getQueryId();
2504  }
2505  }
2506  }
2507  systemAddedAcks |= Message::AckType::Processed;
2508  // for SOW only, we get a completed ack so we know when to remove the handler.
2509  if (!isSubscribeOnly)
2510  {
2511  systemAddedAcks |= Message::AckType::Completed;
2512  }
2513  message_.setAckTypeEnum(requestedAcks | systemAddedAcks);
2514  {
2515  int routesAdded = 0;
2516  Lock<Mutex> l(_lock);
2517  if (!subId.empty() && messageHandler_.isValid())
2518  {
2519  if (!_routes.hasRoute(subId))
2520  {
2521  ++routesAdded;
2522  }
2523  // This can replace a non-subscribe with a matching id
2524  // with a subscription but not another subscription.
2525  _routes.addRoute(subId, messageHandler_, requestedAcks,
2526  systemAddedAcks, isSubscribe);
2527  }
2528  if (!isSubscribeOnly && !qid.empty()
2529  && messageHandler_.isValid() && qid != subId)
2530  {
2531  if (routesAdded == 0)
2532  {
2533  _routes.addRoute(qid, messageHandler_,
2534  requestedAcks, systemAddedAcks, false);
2535  }
2536  else
2537  {
2538  void* data = NULL;
2539  {
2540  Unlock<Mutex> u(_lock);
2541  data = amps_invoke_copy_route_function(
2542  messageHandler_.userData());
2543  }
2544  if (!data)
2545  {
2546  _routes.addRoute(qid, messageHandler_, requestedAcks,
2547  systemAddedAcks, false);
2548  }
2549  else
2550  {
2551  _routes.addRoute(qid,
2552  MessageHandler(messageHandler_.function(),
2553  data),
2554  requestedAcks, systemAddedAcks, false);
2555  }
2556  }
2557  ++routesAdded;
2558  }
2559  if (!id.empty() && messageHandler_.isValid()
2560  && requestedAcks & ~Message::AckType::Persisted
2561  && id != subId && id != qid)
2562  {
2563  if (routesAdded == 0)
2564  {
2565  _routes.addRoute(id, messageHandler_, requestedAcks,
2566  systemAddedAcks, false);
2567  }
2568  else
2569  {
2570  void* data = NULL;
2571  {
2572  Unlock<Mutex> u(_lock);
2573  data = amps_invoke_copy_route_function(
2574  messageHandler_.userData());
2575  }
2576  if (!data)
2577  {
2578  _routes.addRoute(id, messageHandler_, requestedAcks,
2579  systemAddedAcks, false);
2580  }
2581  else
2582  {
2583  _routes.addRoute(id,
2584  MessageHandler(messageHandler_.function(),
2585  data),
2586  requestedAcks,
2587  systemAddedAcks, false);
2588  }
2589  }
2590  ++routesAdded;
2591  }
2592  try
2593  {
2594  // We aren't adding to subscription manager, so this isn't
2595  // an HA subscribe.
2596  syncAckProcessing(timeout_, message_, 0, false);
2597  message_.setAckTypeEnum(requestedAcks);
2598  }
2599  catch (...)
2600  {
2601  _routes.removeRoute(message_.getQueryID());
2602  _routes.removeRoute(message_.getSubscriptionId());
2603  _routes.removeRoute(id);
2604  message_.setAckTypeEnum(requestedAcks);
2605  throw;
2606  }
2607  }
2608  break;
2609  // These are valid commands that are used as-is
2610  case Message::Command::Unsubscribe:
2611  case Message::Command::Heartbeat:
2612  case Message::Command::Logon:
2613  case Message::Command::StartTimer:
2614  case Message::Command::StopTimer:
2615  case Message::Command::SOWDelete:
2616  {
2617  Lock<Mutex> l(_lock);
2618  // if an ack is requested, it'll need a command ID.
2619  if (message_.getAckTypeEnum() != Message::AckType::None)
2620  {
2621  if (id.empty())
2622  {
2623  message_.newCommandId();
2624  id = message_.getCommandId();
2625  }
2626  if (messageHandler_.isValid())
2627  {
2628  _routes.addRoute(id, messageHandler_, requestedAcks,
2629  Message::AckType::None, false);
2630  }
2631  }
2632  _send(message_);
2633  }
2634  break;
2635  case Message::Command::DeltaPublish:
2636  case Message::Command::Publish:
2637  {
2638  bool useSync = message_.getFilter().len() > 0;
2639  Lock<Mutex> l(_lock);
2640  // if an ack is requested, it'll need a command ID.
2641  unsigned ackType = message_.getAckTypeEnum();
2642  if (ackType != Message::AckType::None
2643  || useSync)
2644  {
2645  if (id.empty())
2646  {
2647  message_.newCommandId();
2648  id = message_.getCommandId();
2649  }
2650  if (messageHandler_.isValid())
2651  {
2652  _routes.addRoute(id, messageHandler_, requestedAcks,
2653  Message::AckType::None, false);
2654  }
2655  }
2656  if (useSync)
2657  {
2658  message_.setAckTypeEnum(ackType | Message::AckType::Processed);
2659  syncAckProcessing(timeout_, message_, 0, false);
2660  }
2661  else
2662  {
2663  _send(message_);
2664  }
2665  }
2666  break;
2667  // These are things that shouldn't be sent (not meaningful)
2668  case Message::Command::GroupBegin:
2669  case Message::Command::GroupEnd:
2670  case Message::Command::OOF:
2671  case Message::Command::Ack:
2672  case Message::Command::Unknown:
2673  default:
2674  throw CommandException("Command type " + message_.getCommand() + " can not be sent directly to AMPS");
2675  }
2676  message_.setAckTypeEnum(requestedAcks);
2677  return id;
2678  }
2679 
2680  void setDisconnectHandler(const DisconnectHandler& disconnectHandler)
2681  {
2682  Lock<Mutex> l(_lock);
2683  _disconnectHandler = disconnectHandler;
2684  }
2685 
2686  void setGlobalCommandTypeMessageHandler(const std::string& command_, const MessageHandler& handler_)
2687  {
2688  switch (command_[0])
2689  {
2690 #if 0 // Not currently implemented to avoid an extra branch in delivery
2691  case 'p':
2692  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::Publish] = handler_;
2693  break;
2694  case 's':
2695  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::SOW] = handler_;
2696  break;
2697 #endif
2698  case 'h':
2699  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::Heartbeat] = handler_;
2700  break;
2701 #if 0 // Not currently implemented to avoid an extra branch in delivery
2702  case 'g':
2703  if (command_[6] == 'b')
2704  {
2705  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::GroupBegin] = handler_;
2706  }
2707  else if (command_[6] == 'e')
2708  {
2709  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::GroupEnd] = handler_;
2710  }
2711  else
2712  {
2713  std::ostringstream os;
2714  os << "Invalid command '" << command_ << "' passed to setGlobalCommandTypeHandler";
2715  throw CommandException(os.str());
2716  }
2717  break;
2718  case 'o':
2719  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::OOF] = handler_;
2720  break;
2721 #endif
2722  case 'a':
2723  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::Ack] = handler_;
2724  break;
2725  case 'l':
2726  case 'L':
2727  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::LastChance] = handler_;
2728  break;
2729  case 'd':
2730  case 'D':
2731  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::DuplicateMessage] = handler_;
2732  break;
2733  default:
2734  std::ostringstream os;
2735  os << "Invalid command '" << command_ << "' passed to setGlobalCommandTypeHandler";
2736  throw CommandException(os.str());
2737  break;
2738  }
2739  }
2740 
2741  void setGlobalCommandTypeMessageHandler(const Message::Command::Type command_, const MessageHandler& handler_)
2742  {
2743  switch (command_)
2744  {
2745 #if 0 // Not currently implemented to avoid an extra branch in delivery
2746  case Message::Command::Publish:
2747  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::Publish] = handler_;
2748  break;
2749  case Message::Command::SOW:
2750  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::SOW] = handler_;
2751  break;
2752 #endif
2753  case Message::Command::Heartbeat:
2754  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::Heartbeat] = handler_;
2755  break;
2756 #if 0 // Not currently implemented to avoid an extra branch in delivery
2757  case Message::Command::GroupBegin:
2758  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::GroupBegin] = handler_;
2759  break;
2760  case Message::Command::GroupEnd:
2761  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::GroupEnd] = handler_;
2762  break;
2763  case Message::Command::OOF:
2764  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::OOF] = handler_;
2765  break;
2766 #endif
2767  case Message::Command::Ack:
2768  _globalCommandTypeHandlers[GlobalCommandTypeHandlers::Ack] = handler_;
2769  break;
2770  default:
2771  unsigned bits = 0;
2772  unsigned command = command_;
2773  while (command > 0)
2774  {
2775  ++bits;
2776  command >>= 1;
2777  }
2778  char errBuf[128];
2779  AMPS_snprintf(errBuf, sizeof(errBuf),
2780  "Invalid command '%.*s' passed to setGlobalCommandTypeHandler",
2781  CommandConstants<0>::Lengths[bits],
2782  CommandConstants<0>::Values[bits]);
2783  throw CommandException(errBuf);
2784  break;
2785  }
2786  }
2787 
2788  void setGlobalCommandTypeMessageHandler(const GlobalCommandTypeHandlers handlerType_, const MessageHandler& handler_)
2789  {
2790  _globalCommandTypeHandlers[handlerType_] = handler_;
2791  }
2792 
2793  void setFailedWriteHandler(FailedWriteHandler* handler_)
2794  {
2795  Lock<Mutex> l(_lock);
2796  _failedWriteHandler.reset(handler_);
2797  }
2798 
2799  void setPublishStore(const Store& publishStore_)
2800  {
2801  Lock<Mutex> l(_lock);
2802  if (_connected)
2803  {
2804  throw AlreadyConnectedException("Setting a publish store on a connected client is undefined behavior");
2805  }
2806  _publishStore = publishStore_;
2807  }
2808 
2809  void setBookmarkStore(const BookmarkStore& bookmarkStore_)
2810  {
2811  Lock<Mutex> l(_lock);
2812  if (_connected)
2813  {
2814  throw AlreadyConnectedException("Setting a bookmark store on a connected client is undefined behavior");
2815  }
2816  _bookmarkStore = bookmarkStore_;
2817  }
2818 
2819  void setSubscriptionManager(SubscriptionManager* subscriptionManager_)
2820  {
2821  Lock<Mutex> l(_lock);
2822  _subscriptionManager.reset(subscriptionManager_);
2823  }
2824 
2825  SubscriptionManager* getSubscriptionManager() const
2826  {
2827  return const_cast<SubscriptionManager*>(_subscriptionManager.get());
2828  }
2829 
2830  DisconnectHandler getDisconnectHandler() const
2831  {
2832  return _disconnectHandler;
2833  }
2834 
2835  MessageHandler getDuplicateMessageHandler() const
2836  {
2837  return _globalCommandTypeHandlers[GlobalCommandTypeHandlers::DuplicateMessage];
2838  }
2839 
2840  FailedWriteHandler* getFailedWriteHandler() const
2841  {
2842  return const_cast<FailedWriteHandler*>(_failedWriteHandler.get());
2843  }
2844 
2845  Store getPublishStore() const
2846  {
2847  return _publishStore;
2848  }
2849 
2850  BookmarkStore getBookmarkStore() const
2851  {
2852  return _bookmarkStore;
2853  }
2854 
2855  amps_uint64_t publish(const char* topic_, size_t topicLen_, const char* data_, size_t dataLen_)
2856  {
2857  if (!_publishStore.isValid())
2858  {
2859  Lock<Mutex> l(_lock);
2860  _publishMessage.assignTopic(topic_, topicLen_);
2861  _publishMessage.assignData(data_, dataLen_);
2862  _send(_publishMessage);
2863  return 0;
2864  }
2865  else
2866  {
2867  if (!publishStoreMessage)
2868  {
2869  publishStoreMessage = new Message();
2870  PerThreadMessageTracker::addMessageToCleanupList(publishStoreMessage);
2871  }
2872  publishStoreMessage->reset();
2873  publishStoreMessage->setCommandEnum(Message::Command::Publish);
2874  return _publish(topic_, topicLen_, data_, dataLen_);
2875  }
2876  }
2877 
2878  amps_uint64_t publish(const char* topic_, size_t topicLen_, const char* data_,
2879  size_t dataLen_, unsigned long expiration_)
2880  {
2881  if (!_publishStore.isValid())
2882  {
2883  Lock<Mutex> l(_lock);
2884  _publishMessage.assignTopic(topic_, topicLen_);
2885  _publishMessage.assignData(data_, dataLen_);
2886  char exprBuf[AMPS_NUMBER_BUFFER_LEN];
2887  size_t pos = convertToCharArray(exprBuf, expiration_);
2888  _publishMessage.assignExpiration(exprBuf + pos, AMPS_NUMBER_BUFFER_LEN - pos);
2889  _send(_publishMessage);
2890  _publishMessage.assignExpiration(NULL, 0);
2891  return 0;
2892  }
2893  else
2894  {
2895  if (!publishStoreMessage)
2896  {
2897  publishStoreMessage = new Message();
2898  PerThreadMessageTracker::addMessageToCleanupList(publishStoreMessage);
2899  }
2900  publishStoreMessage->reset();
2901  char exprBuf[AMPS_NUMBER_BUFFER_LEN];
2902  size_t exprPos = convertToCharArray(exprBuf, expiration_);
2903  publishStoreMessage->setCommandEnum(Message::Command::Publish)
2904  .assignExpiration(exprBuf + exprPos,
2905  AMPS_NUMBER_BUFFER_LEN - exprPos);
2906  return _publish(topic_, topicLen_, data_, dataLen_);
2907  }
2908  }
2909 
2910  class FlushAckHandler : ConnectionStateListener
2911  {
2912  private:
2913  ClientImpl* _pClient;
2914  Field _cmdId;
2915 #if __cplusplus >= 201100L || _MSC_VER >= 1900
2916  std::atomic<bool> _acked;
2917  std::atomic<bool> _disconnected;
2918 #else
2919  volatile bool _acked;
2920  volatile bool _disconnected;
2921 #endif
2922  public:
2923  FlushAckHandler(ClientImpl* pClient_)
2924  : _pClient(pClient_), _cmdId(), _acked(false), _disconnected(false)
2925  {
2926  pClient_->addConnectionStateListener(this);
2927  }
2928  ~FlushAckHandler()
2929  {
2930  _pClient->removeConnectionStateListener(this);
2931  _pClient->removeMessageHandler(_cmdId);
2932  _cmdId.clear();
2933  }
2934  void setCommandId(const Field& cmdId_)
2935  {
2936  _cmdId.deepCopy(cmdId_);
2937  }
2938  void invoke(const Message&)
2939  {
2940  _acked = true;
2941  }
2942  void connectionStateChanged(State state_)
2943  {
2944  if (state_ <= Shutdown)
2945  {
2946  _disconnected = true;
2947  }
2948  }
2949  bool acked()
2950  {
2951  return _acked;
2952  }
2953  bool done()
2954  {
2955  return _acked || _disconnected;
2956  }
2957  };
2958 
2959  void publishFlush(long timeout_, unsigned ackType_)
2960  {
2961  static const char* processed = "processed";
2962  static const size_t processedLen = strlen(processed);
2963  static const char* persisted = "persisted";
2964  static const size_t persistedLen = strlen(persisted);
2965  static const char* flush = "flush";
2966  static const size_t flushLen = strlen(flush);
2967  static VersionInfo minPersisted("5.3.3.0");
2968  static VersionInfo minFlush("4");
2969  if (ackType_ != Message::AckType::Processed
2970  && ackType_ != Message::AckType::Persisted)
2971  {
2972  throw CommandException("Flush can only be used with processed or persisted acks.");
2973  }
2974  FlushAckHandler flushHandler(this);
2975  if (_serverVersion >= minFlush)
2976  {
2977  Lock<Mutex> l(_lock);
2978  if (!_connected)
2979  {
2980  throw DisconnectedException("Not connected trying to flush");
2981  }
2982  _message.reset();
2983  _message.newCommandId();
2984  _message.assignCommand(flush, flushLen);
2985  if (_serverVersion < minPersisted
2986  || ackType_ == Message::AckType::Processed)
2987  {
2988  _message.assignAckType(processed, processedLen);
2989  }
2990  else
2991  {
2992  _message.assignAckType(persisted, persistedLen);
2993  }
2994  flushHandler.setCommandId(_message.getCommandId());
2995  addMessageHandler(_message.getCommandId(),
2996  std::bind(&FlushAckHandler::invoke,
2997  std::ref(flushHandler),
2998  std::placeholders::_1),
2999  ackType_, false);
3000  NoDelay noDelay(_client);
3001  if (_send(_message) == -1)
3002  {
3003  throw DisconnectedException("Disconnected trying to flush");
3004  }
3005  }
3006  if (_publishStore.isValid())
3007  {
3008  try
3009  {
3010  _publishStore.flush(timeout_);
3011  }
3012  catch (const AMPSException& ex)
3013  {
3014  AMPS_UNHANDLED_EXCEPTION(ex);
3015  throw;
3016  }
3017  }
3018  else if (_serverVersion < minFlush)
3019  {
3020  if (timeout_ > 0)
3021  {
3022  AMPS_USLEEP(timeout_ * 1000);
3023  }
3024  else
3025  {
3026  AMPS_USLEEP(1000 * 1000);
3027  }
3028  return;
3029  }
3030  if (timeout_)
3031  {
3032  Timer timer((double)timeout_);
3033  timer.start();
3034  while (!timer.check() && !flushHandler.done())
3035  {
3036  AMPS_USLEEP(10000);
3037  amps_invoke_waiting_function();
3038  }
3039  }
3040  else
3041  {
3042  while (!flushHandler.done())
3043  {
3044  AMPS_USLEEP(10000);
3045  amps_invoke_waiting_function();
3046  }
3047  }
3048  // No response or disconnect in timeout interval
3049  if (!flushHandler.done())
3050  {
3051  throw TimedOutException("Timed out waiting for flush");
3052  }
3053  // We got disconnected and there is no publish store
3054  if (!flushHandler.acked() && !_publishStore.isValid())
3055  {
3056  throw DisconnectedException("Disconnected waiting for flush");
3057  }
3058  }
3059 
3060  amps_uint64_t deltaPublish(const char* topic_, size_t topicLength_,
3061  const char* data_, size_t dataLength_)
3062  {
3063  if (!_publishStore.isValid())
3064  {
3065  Lock<Mutex> l(_lock);
3066  _deltaMessage.assignTopic(topic_, topicLength_);
3067  _deltaMessage.assignData(data_, dataLength_);
3068  _send(_deltaMessage);
3069  return 0;
3070  }
3071  else
3072  {
3073  if (!publishStoreMessage)
3074  {
3075  publishStoreMessage = new Message();
3076  PerThreadMessageTracker::addMessageToCleanupList(publishStoreMessage);
3077  }
3078  publishStoreMessage->reset();
3079  publishStoreMessage->setCommandEnum(Message::Command::DeltaPublish);
3080  return _publish(topic_, topicLength_, data_, dataLength_);
3081  }
3082  }
3083 
3084  amps_uint64_t deltaPublish(const char* topic_, size_t topicLength_,
3085  const char* data_, size_t dataLength_,
3086  unsigned long expiration_)
3087  {
3088  if (!_publishStore.isValid())
3089  {
3090  Lock<Mutex> l(_lock);
3091  _deltaMessage.assignTopic(topic_, topicLength_);
3092  _deltaMessage.assignData(data_, dataLength_);
3093  char exprBuf[AMPS_NUMBER_BUFFER_LEN];
3094  size_t pos = convertToCharArray(exprBuf, expiration_);
3095  _deltaMessage.assignExpiration(exprBuf + pos, AMPS_NUMBER_BUFFER_LEN - pos);
3096  _send(_deltaMessage);
3097  _deltaMessage.assignExpiration(NULL, 0);
3098  return 0;
3099  }
3100  else
3101  {
3102  if (!publishStoreMessage)
3103  {
3104  publishStoreMessage = new Message();
3105  PerThreadMessageTracker::addMessageToCleanupList(publishStoreMessage);
3106  }
3107  publishStoreMessage->reset();
3108  char exprBuf[AMPS_NUMBER_BUFFER_LEN];
3109  size_t exprPos = convertToCharArray(exprBuf, expiration_);
3110  publishStoreMessage->setCommandEnum(Message::Command::DeltaPublish)
3111  .assignExpiration(exprBuf + exprPos,
3112  AMPS_NUMBER_BUFFER_LEN - exprPos);
3113  return _publish(topic_, topicLength_, data_, dataLength_);
3114  }
3115  }
3116 
3117  amps_uint64_t _publish(const char* topic_, size_t topicLength_,
3118  const char* data_, size_t dataLength_)
3119  {
3120  publishStoreMessage->assignTopic(topic_, topicLength_)
3121  .setAckTypeEnum(Message::AckType::Persisted)
3122  .assignData(data_, dataLength_);
3123  amps_uint64_t haSequenceNumber = _publishStore.store(*publishStoreMessage);
3124  char buf[AMPS_NUMBER_BUFFER_LEN];
3125  size_t pos = convertToCharArray(buf, haSequenceNumber);
3126  publishStoreMessage->assignSequence(buf + pos, AMPS_NUMBER_BUFFER_LEN - pos);
3127  {
3128  Lock<Mutex> l(_lock);
3129  _send(*publishStoreMessage, haSequenceNumber);
3130  }
3131  return haSequenceNumber;
3132  }
3133 
3134  virtual std::string logon(long timeout_, Authenticator& authenticator_,
3135  const char* options_ = NULL)
3136  {
3137  Lock<Mutex> l(_lock);
3138  return _logon(timeout_, authenticator_, options_);
3139  }
3140 
3141  virtual std::string _logon(long timeout_, Authenticator& authenticator_,
3142  const char* options_ = NULL)
3143  {
3144  AtomicFlagFlip pubFlip(&_badTimeToHAPublish);
3145  _message.reset();
3146  _message.setCommandEnum(Message::Command::Logon);
3147  _message.newCommandId();
3148  std::string newCommandId = _message.getCommandId();
3149  _message.setClientName(_name);
3150 #ifdef AMPS_CLIENT_VERSION_WITH_LANGUAGE
3151  _message.assignVersion(AMPS_CLIENT_VERSION_WITH_LANGUAGE,
3152  strlen(AMPS_CLIENT_VERSION_WITH_LANGUAGE));
3153 #endif
3154  URI uri(_lastUri);
3155  if (uri.user().size())
3156  {
3157  _message.setUserId(uri.user());
3158  }
3159  if (uri.password().size())
3160  {
3161  _message.setPassword(uri.password());
3162  }
3163  if (uri.protocol() == "amps" && uri.messageType().size())
3164  {
3165  _message.setMessageType(uri.messageType());
3166  }
3167  if (uri.isTrue("pretty"))
3168  {
3169  _message.setOptions("pretty");
3170  }
3171 
3172  _message.setPassword(authenticator_.authenticate(_message.getUserId(), _message.getPassword()));
3173  if (!_logonCorrelationData.empty())
3174  {
3175  _message.assignCorrelationId(_logonCorrelationData);
3176  }
3177  if (options_)
3178  {
3179  _message.setOptions(options_);
3180  }
3181  _username = _message.getUserId();
3182  try
3183  {
3184  NoDelay noDelay(_client);
3185  while (true)
3186  {
3187  _message.setAckTypeEnum(Message::AckType::Processed);
3188  AckResponse ack = syncAckProcessing(timeout_, _message);
3189  if (ack.status() == "retry")
3190  {
3191  _message.setPassword(authenticator_.retry(ack.username(), ack.password()));
3192  _username = ack.username();
3193  _message.setUserId(_username);
3194  }
3195  else
3196  {
3197  authenticator_.completed(ack.username(), ack.password(), ack.reason());
3198  break;
3199  }
3200  }
3201  broadcastConnectionStateChanged(ConnectionStateListener::LoggedOn);
3202 
3203  // Now re-send the heartbeat command if configured
3204  _sendHeartbeat();
3205  }
3206  catch (const AMPSException& ex)
3207  {
3208  _lock.signalAll();
3209  AMPS_UNHANDLED_EXCEPTION(ex);
3210  throw;
3211  }
3212  catch (...)
3213  {
3214  _lock.signalAll();
3215  throw;
3216  }
3217 
3218  if (_publishStore.isValid())
3219  {
3220  try
3221  {
3222  _publishStore.replay(_replayer);
3223  broadcastConnectionStateChanged(ConnectionStateListener::PublishReplayed);
3224  }
3225  catch (const PublishStoreGapException& ex)
3226  {
3227  _lock.signalAll();
3228  AMPS_UNHANDLED_EXCEPTION(ex);
3229  throw ex;
3230  }
3231  catch (const StoreException& ex)
3232  {
3233  _lock.signalAll();
3234  std::ostringstream os;
3235  os << "A local store exception occurred while logging on."
3236  << ex.toString();
3237  throw ConnectionException(os.str());
3238  }
3239  catch (const AMPSException& ex)
3240  {
3241  _lock.signalAll();
3242  AMPS_UNHANDLED_EXCEPTION(ex);
3243  throw ex;
3244  }
3245  catch (const std::exception& ex)
3246  {
3247  _lock.signalAll();
3248  AMPS_UNHANDLED_EXCEPTION(ex);
3249  throw ex;
3250  }
3251  catch (...)
3252  {
3253  _lock.signalAll();
3254  throw;
3255  }
3256  }
3257  _lock.signalAll();
3258  return newCommandId;
3259  }
3260 
3261  std::string subscribe(const MessageHandler& messageHandler_,
3262  const std::string& topic_,
3263  long timeout_,
3264  const std::string& filter_,
3265  const std::string& bookmark_,
3266  const std::string& options_,
3267  const std::string& subId_,
3268  bool isHASubscribe_ = true)
3269  {
3270  isHASubscribe_ &= (bool)_subscriptionManager;
3271  Lock<Mutex> l(_lock);
3272  _message.reset();
3273  _message.setCommandEnum(Message::Command::Subscribe);
3274  _message.newCommandId();
3275  std::string subId(subId_);
3276  if (subId.empty())
3277  {
3278  if (options_.find(AMPS_OPTIONS_REPLACE, 0, strlen(AMPS_OPTIONS_REPLACE) - 1) != std::string::npos)
3279  {
3280  throw ConnectionException("Cannot issue a replacement subscription; a valid subscription id is required.");
3281  }
3282 
3283  subId = _message.getCommandId();
3284  }
3285  _message.setSubscriptionId(subId);
3286  // we need to deep copy this before sending the message; while we are
3287  // waiting for a response, the fields in _message may get blown away for
3288  // other operations.
3289  AMPS::Message::Field subIdField(subId);
3290  unsigned ackTypes = Message::AckType::Processed;
3291 
3292  if (!bookmark_.empty() && _bookmarkStore.isValid())
3293  {
3294  ackTypes |= Message::AckType::Persisted;
3295  }
3296  _message.setTopic(topic_);
3297 
3298  if (filter_.length())
3299  {
3300  _message.setFilter(filter_);
3301  }
3302  if (bookmark_.length())
3303  {
3304  if (bookmark_ == AMPS_BOOKMARK_RECENT)
3305  {
3306  Message::Field mostRecent = _bookmarkStore.getMostRecent(subIdField);
3307  _message.setBookmark(mostRecent);
3308  }
3309  else
3310  {
3311  _message.setBookmark(bookmark_);
3312  if (_bookmarkStore.isValid())
3313  {
3314  if (bookmark_ != AMPS_BOOKMARK_NOW &&
3315  bookmark_ != AMPS_BOOKMARK_EPOCH)
3316  {
3317  _bookmarkStore.log(_message);
3318  _bookmarkStore.discard(_message);
3319  _bookmarkStore.persisted(subIdField, _message.getBookmark());
3320  }
3321  }
3322  }
3323  }
3324  if (options_.length())
3325  {
3326  _message.setOptions(options_);
3327  }
3328 
3329  Message message = _message;
3330  if (isHASubscribe_)
3331  {
3332  message = _message.deepCopy();
3333  Unlock<Mutex> u(_lock);
3334  _subscriptionManager->subscribe(messageHandler_, message,
3335  Message::AckType::None);
3336  if (_badTimeToHASubscribe)
3337  {
3338  return subId;
3339  }
3340  }
3341  if (!_routes.hasRoute(_message.getSubscriptionId()))
3342  {
3343  _routes.addRoute(_message.getSubscriptionId(), messageHandler_,
3344  Message::AckType::None, ackTypes, true);
3345  }
3346  message.setAckTypeEnum(ackTypes);
3347  if (!options_.empty())
3348  {
3349  message.setOptions(options_);
3350  }
3351  try
3352  {
3353  syncAckProcessing(timeout_, message, isHASubscribe_);
3354  }
3355  catch (const DisconnectedException&)
3356  {
3357  if (!isHASubscribe_)
3358  {
3359  _routes.removeRoute(subIdField);
3360  throw;
3361  }
3362  else
3363  {
3364  AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(subIdField));
3365  throw;
3366  }
3367  }
3368  catch (const TimedOutException&)
3369  {
3370  AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(subIdField));
3371  throw;
3372  }
3373  catch (...)
3374  {
3375  if (isHASubscribe_)
3376  {
3377  // Have to unlock before calling into sub manager to avoid deadlock
3378  Unlock<Mutex> unlock(_lock);
3379  _subscriptionManager->unsubscribe(subIdField);
3380  }
3381  _routes.removeRoute(subIdField);
3382  throw;
3383  }
3384 
3385  return subId;
3386  }
3387  std::string deltaSubscribe(const MessageHandler& messageHandler_,
3388  const std::string& topic_,
3389  long timeout_,
3390  const std::string& filter_,
3391  const std::string& bookmark_,
3392  const std::string& options_,
3393  const std::string& subId_ = "",
3394  bool isHASubscribe_ = true)
3395  {
3396  isHASubscribe_ &= (bool)_subscriptionManager;
3397  Lock<Mutex> l(_lock);
3398  _message.reset();
3399  _message.setCommandEnum(Message::Command::DeltaSubscribe);
3400  _message.newCommandId();
3401  std::string subId(subId_);
3402  if (subId.empty())
3403  {
3404  subId = _message.getCommandId();
3405  }
3406  _message.setSubscriptionId(subId);
3407  // we need to deep copy this before sending the message; while we are
3408  // waiting for a response, the fields in _message may get blown away for
3409  // other operations.
3410  AMPS::Message::Field subIdField(subId);
3411  unsigned ackTypes = Message::AckType::Processed;
3412 
3413  if (!bookmark_.empty() && _bookmarkStore.isValid())
3414  {
3415  ackTypes |= Message::AckType::Persisted;
3416  }
3417  _message.setTopic(topic_);
3418  if (filter_.length())
3419  {
3420  _message.setFilter(filter_);
3421  }
3422  if (bookmark_.length())
3423  {
3424  if (bookmark_ == AMPS_BOOKMARK_RECENT)
3425  {
3426  Message::Field mostRecent = _bookmarkStore.getMostRecent(subIdField);
3427  _message.setBookmark(mostRecent);
3428  }
3429  else
3430  {
3431  _message.setBookmark(bookmark_);
3432  if (_bookmarkStore.isValid())
3433  {
3434  if (bookmark_ != AMPS_BOOKMARK_NOW &&
3435  bookmark_ != AMPS_BOOKMARK_EPOCH)
3436  {
3437  _bookmarkStore.log(_message);
3438  _bookmarkStore.discard(_message);
3439  _bookmarkStore.persisted(subIdField, _message.getBookmark());
3440  }
3441  }
3442  }
3443  }
3444  if (options_.length())
3445  {
3446  _message.setOptions(options_);
3447  }
3448  Message message = _message;
3449  if (isHASubscribe_)
3450  {
3451  message = _message.deepCopy();
3452  Unlock<Mutex> u(_lock);
3453  _subscriptionManager->subscribe(messageHandler_, message,
3454  Message::AckType::None);
3455  if (_badTimeToHASubscribe)
3456  {
3457  return subId;
3458  }
3459  }
3460  if (!_routes.hasRoute(_message.getSubscriptionId()))
3461  {
3462  _routes.addRoute(_message.getSubscriptionId(), messageHandler_,
3463  Message::AckType::None, ackTypes, true);
3464  }
3465  message.setAckTypeEnum(ackTypes);
3466  if (!options_.empty())
3467  {
3468  message.setOptions(options_);
3469  }
3470  try
3471  {
3472  syncAckProcessing(timeout_, message, isHASubscribe_);
3473  }
3474  catch (const DisconnectedException&)
3475  {
3476  if (!isHASubscribe_)
3477  {
3478  _routes.removeRoute(subIdField);
3479  throw;
3480  }
3481  }
3482  catch (const TimedOutException&)
3483  {
3484  AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(subIdField));
3485  throw;
3486  }
3487  catch (...)
3488  {
3489  if (isHASubscribe_)
3490  {
3491  // Have to unlock before calling into sub manager to avoid deadlock
3492  Unlock<Mutex> unlock(_lock);
3493  _subscriptionManager->unsubscribe(subIdField);
3494  }
3495  _routes.removeRoute(subIdField);
3496  throw;
3497  }
3498  return subId;
3499  }
3500 
3501  void unsubscribe(const std::string& id)
3502  {
3503  Lock<Mutex> l(_lock);
3504  unsubscribeInternal(id);
3505  }
3506 
3507  void unsubscribe(void)
3508  {
3509  if (_subscriptionManager)
3510  {
3511  _subscriptionManager->clear();
3512  }
3513  {
3514  _routes.unsubscribeAll();
3515  Lock<Mutex> l(_lock);
3516  _message.reset();
3517  _message.setCommandEnum(Message::Command::Unsubscribe);
3518  _message.newCommandId();
3519  _message.setSubscriptionId("all");
3520  _sendWithoutRetry(_message);
3521  }
3522  deferredExecution(&amps_noOpFn, NULL);
3523  }
3524 
3525  std::string sow(const MessageHandler& messageHandler_,
3526  const std::string& topic_,
3527  const std::string& filter_ = "",
3528  const std::string& orderBy_ = "",
3529  const std::string& bookmark_ = "",
3530  int batchSize_ = AMPS_DEFAULT_BATCH_SIZE,
3531  int topN_ = AMPS_DEFAULT_TOP_N,
3532  const std::string& options_ = "",
3533  long timeout_ = AMPS_DEFAULT_COMMAND_TIMEOUT)
3534  {
3535  Lock<Mutex> l(_lock);
3536  _message.reset();
3537  _message.setCommandEnum(Message::Command::SOW);
3538  _message.newCommandId();
3539  // need to keep our own copy of the command ID.
3540  std::string commandId = _message.getCommandId();
3541  _message.setQueryID(_message.getCommandId());
3542  unsigned ackTypes = Message::AckType::Processed | Message::AckType::Completed;
3543  _message.setAckTypeEnum(ackTypes);
3544  _message.setTopic(topic_);
3545  if (filter_.length())
3546  {
3547  _message.setFilter(filter_);
3548  }
3549  if (orderBy_.length())
3550  {
3551  _message.setOrderBy(orderBy_);
3552  }
3553  if (bookmark_.length())
3554  {
3555  _message.setBookmark(bookmark_);
3556  }
3557  _message.setBatchSize(AMPS::asString(batchSize_));
3558  if (topN_ != AMPS_DEFAULT_TOP_N)
3559  {
3560  _message.setTopNRecordsReturned(AMPS::asString(topN_));
3561  }
3562  if (options_.length())
3563  {
3564  _message.setOptions(options_);
3565  }
3566 
3567  _routes.addRoute(_message.getQueryID(), messageHandler_,
3568  Message::AckType::None, ackTypes, false);
3569 
3570  try
3571  {
3572  syncAckProcessing(timeout_, _message);
3573  }
3574  catch (...)
3575  {
3576  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId));
3577  throw;
3578  }
3579 
3580  return commandId;
3581  }
3582 
3583  std::string sow(const MessageHandler& messageHandler_,
3584  const std::string& topic_,
3585  long timeout_,
3586  const std::string& filter_ = "",
3587  int batchSize_ = AMPS_DEFAULT_BATCH_SIZE,
3588  int topN_ = AMPS_DEFAULT_TOP_N)
3589  {
3590  std::string notSet;
3591  return sow(messageHandler_,
3592  topic_,
3593  filter_,
3594  notSet, // orderBy
3595  notSet, // bookmark
3596  batchSize_,
3597  topN_,
3598  notSet,
3599  timeout_);
3600  }
3601 
3602  std::string sowAndSubscribe(const MessageHandler& messageHandler_,
3603  const std::string& topic_,
3604  const std::string& filter_ = "",
3605  const std::string& orderBy_ = "",
3606  const std::string& bookmark_ = "",
3607  int batchSize_ = AMPS_DEFAULT_BATCH_SIZE,
3608  int topN_ = AMPS_DEFAULT_TOP_N,
3609  const std::string& options_ = "",
3610  long timeout_ = AMPS_DEFAULT_COMMAND_TIMEOUT,
3611  bool isHASubscribe_ = true)
3612  {
3613  isHASubscribe_ &= (bool)_subscriptionManager;
3614  unsigned ackTypes = Message::AckType::Processed;
3615  Lock<Mutex> l(_lock);
3616  _message.reset();
3617  _message.setCommandEnum(Message::Command::SOWAndSubscribe);
3618  _message.newCommandId();
3619  Field cid = _message.getCommandId();
3620  std::string subId = cid;
3621  _message.setQueryID(cid).setSubscriptionId(cid).setTopic(topic_);
3622  if (filter_.length())
3623  {
3624  _message.setFilter(filter_);
3625  }
3626  if (orderBy_.length())
3627  {
3628  _message.setOrderBy(orderBy_);
3629  }
3630  if (bookmark_.length())
3631  {
3632  _message.setBookmark(bookmark_);
3633  Message::Field bookmark = _message.getBookmark();
3634  if (_bookmarkStore.isValid())
3635  {
3636  ackTypes |= Message::AckType::Persisted;
3637  if (bookmark == AMPS_BOOKMARK_RECENT)
3638  {
3639  _message.setBookmark(_bookmarkStore.getMostRecent(_message.getSubscriptionId()));
3640  }
3641  else if (bookmark != AMPS_BOOKMARK_NOW &&
3642  bookmark != AMPS_BOOKMARK_EPOCH)
3643  {
3644  _bookmarkStore.log(_message);
3645  if (!BookmarkRange::isRange(bookmark))
3646  {
3647  _bookmarkStore.discard(_message);
3648  _bookmarkStore.persisted(_message.getSubscriptionId(),
3649  bookmark);
3650  }
3651  }
3652  }
3653  else if (bookmark == AMPS_BOOKMARK_RECENT)
3654  {
3655  _message.setBookmark(AMPS_BOOKMARK_EPOCH);
3656  }
3657  }
3658  _message.setBatchSize(AMPS::asString(batchSize_));
3659  if (topN_ != AMPS_DEFAULT_TOP_N)
3660  {
3661  _message.setTopNRecordsReturned(AMPS::asString(topN_));
3662  }
3663  if (options_.length())
3664  {
3665  _message.setOptions(options_);
3666  }
3667 
3668  Message message = _message;
3669  if (isHASubscribe_)
3670  {
3671  message = _message.deepCopy();
3672  Unlock<Mutex> u(_lock);
3673  _subscriptionManager->subscribe(messageHandler_, message,
3674  Message::AckType::None);
3675  if (_badTimeToHASubscribe)
3676  {
3677  return subId;
3678  }
3679  }
3680  _routes.addRoute(cid, messageHandler_,
3681  Message::AckType::None, ackTypes, true);
3682  message.setAckTypeEnum(ackTypes);
3683  if (!options_.empty())
3684  {
3685  message.setOptions(options_);
3686  }
3687  try
3688  {
3689  syncAckProcessing(timeout_, message, isHASubscribe_);
3690  }
3691  catch (const DisconnectedException&)
3692  {
3693  if (!isHASubscribe_)
3694  {
3695  _routes.removeRoute(subId);
3696  throw;
3697  }
3698  }
3699  catch (const TimedOutException&)
3700  {
3701  AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(subId));
3702  throw;
3703  }
3704  catch (...)
3705  {
3706  if (isHASubscribe_)
3707  {
3708  // Have to unlock before calling into sub manager to avoid deadlock
3709  Unlock<Mutex> unlock(_lock);
3710  _subscriptionManager->unsubscribe(cid);
3711  }
3712  _routes.removeRoute(subId);
3713  throw;
3714  }
3715  return subId;
3716  }
3717 
3718  std::string sowAndSubscribe(const MessageHandler& messageHandler_,
3719  const std::string& topic_,
3720  long timeout_,
3721  const std::string& filter_ = "",
3722  int batchSize_ = AMPS_DEFAULT_BATCH_SIZE,
3723  bool oofEnabled_ = false,
3724  int topN_ = AMPS_DEFAULT_TOP_N,
3725  bool isHASubscribe_ = true)
3726  {
3727  std::string notSet;
3728  return sowAndSubscribe(messageHandler_,
3729  topic_,
3730  filter_,
3731  notSet, // orderBy
3732  notSet, // bookmark
3733  batchSize_,
3734  topN_,
3735  (oofEnabled_ ? "oof" : ""),
3736  timeout_,
3737  isHASubscribe_);
3738  }
3739 
3740  std::string sowAndDeltaSubscribe(const MessageHandler& messageHandler_,
3741  const std::string& topic_,
3742  const std::string& filter_ = "",
3743  const std::string& orderBy_ = "",
3744  int batchSize_ = AMPS_DEFAULT_BATCH_SIZE,
3745  int topN_ = AMPS_DEFAULT_TOP_N,
3746  const std::string& options_ = "",
3747  long timeout_ = AMPS_DEFAULT_COMMAND_TIMEOUT,
3748  bool isHASubscribe_ = true)
3749  {
3750  isHASubscribe_ &= (bool)_subscriptionManager;
3751  Lock<Mutex> l(_lock);
3752  _message.reset();
3753  _message.setCommandEnum(Message::Command::SOWAndDeltaSubscribe);
3754  _message.newCommandId();
3755  _message.setQueryID(_message.getCommandId());
3756  _message.setSubscriptionId(_message.getCommandId());
3757  std::string subId = _message.getSubscriptionId();
3758  _message.setTopic(topic_);
3759  if (filter_.length())
3760  {
3761  _message.setFilter(filter_);
3762  }
3763  if (orderBy_.length())
3764  {
3765  _message.setOrderBy(orderBy_);
3766  }
3767  _message.setBatchSize(AMPS::asString(batchSize_));
3768  if (topN_ != AMPS_DEFAULT_TOP_N)
3769  {
3770  _message.setTopNRecordsReturned(AMPS::asString(topN_));
3771  }
3772  if (options_.length())
3773  {
3774  _message.setOptions(options_);
3775  }
3776  Message message = _message;
3777  if (isHASubscribe_)
3778  {
3779  message = _message.deepCopy();
3780  Unlock<Mutex> u(_lock);
3781  _subscriptionManager->subscribe(messageHandler_, message,
3782  Message::AckType::None);
3783  if (_badTimeToHASubscribe)
3784  {
3785  return subId;
3786  }
3787  }
3788  _routes.addRoute(message.getQueryID(), messageHandler_,
3789  Message::AckType::None, Message::AckType::Processed, true);
3790  message.setAckTypeEnum(Message::AckType::Processed);
3791  if (!options_.empty())
3792  {
3793  message.setOptions(options_);
3794  }
3795  try
3796  {
3797  syncAckProcessing(timeout_, message, isHASubscribe_);
3798  }
3799  catch (const DisconnectedException&)
3800  {
3801  if (!isHASubscribe_)
3802  {
3803  _routes.removeRoute(subId);
3804  throw;
3805  }
3806  }
3807  catch (const TimedOutException&)
3808  {
3809  AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(subId));
3810  throw;
3811  }
3812  catch (...)
3813  {
3814  if (isHASubscribe_)
3815  {
3816  // Have to unlock before calling into sub manager to avoid deadlock
3817  Unlock<Mutex> unlock(_lock);
3818  _subscriptionManager->unsubscribe(Field(subId));
3819  }
3820  _routes.removeRoute(subId);
3821  throw;
3822  }
3823  return subId;
3824  }
3825 
3826  std::string sowAndDeltaSubscribe(const MessageHandler& messageHandler_,
3827  const std::string& topic_,
3828  long timeout_,
3829  const std::string& filter_ = "",
3830  int batchSize_ = AMPS_DEFAULT_BATCH_SIZE,
3831  bool oofEnabled_ = false,
3832  bool sendEmpties_ = false,
3833  int topN_ = AMPS_DEFAULT_TOP_N,
3834  bool isHASubscribe_ = true)
3835  {
3836  std::string notSet;
3837  Message::Options options;
3838  if (oofEnabled_)
3839  {
3840  options.setOOF();
3841  }
3842  if (sendEmpties_ == false)
3843  {
3844  options.setNoEmpties();
3845  }
3846  return sowAndDeltaSubscribe(messageHandler_,
3847  topic_,
3848  filter_,
3849  notSet, // orderBy
3850  batchSize_,
3851  topN_,
3852  options,
3853  timeout_,
3854  isHASubscribe_);
3855  }
3856 
3857  std::string sowDelete(const MessageHandler& messageHandler_,
3858  const std::string& topic_,
3859  const std::string& filter_,
3860  long timeout_,
3861  Message::Field commandId_ = Message::Field())
3862  {
3863  if (_publishStore.isValid())
3864  {
3865  unsigned ackType = Message::AckType::Processed |
3866  Message::AckType::Stats |
3867  Message::AckType::Persisted;
3868  if (!publishStoreMessage)
3869  {
3870  publishStoreMessage = new Message();
3871  PerThreadMessageTracker::addMessageToCleanupList(publishStoreMessage);
3872  }
3873  publishStoreMessage->reset();
3874  if (commandId_.empty())
3875  {
3876  publishStoreMessage->newCommandId();
3877  commandId_ = publishStoreMessage->getCommandId();
3878  }
3879  else
3880  {
3881  publishStoreMessage->setCommandId(commandId_.data(), commandId_.len());
3882  }
3883  publishStoreMessage->setCommandEnum(Message::Command::SOWDelete)
3884  .assignSubscriptionId(commandId_.data(), commandId_.len())
3885  .assignQueryID(commandId_.data(), commandId_.len())
3886  .setAckTypeEnum(ackType)
3887  .assignTopic(topic_.c_str(), topic_.length())
3888  .assignFilter(filter_.c_str(), filter_.length());
3889  amps_uint64_t haSequenceNumber = _publishStore.store(*publishStoreMessage);
3890  char buf[AMPS_NUMBER_BUFFER_LEN];
3891  size_t pos = convertToCharArray(buf, haSequenceNumber);
3892  publishStoreMessage->assignSequence(buf + pos, AMPS_NUMBER_BUFFER_LEN - pos);
3893  {
3894  try
3895  {
3896  Lock<Mutex> l(_lock);
3897  _routes.addRoute(commandId_, messageHandler_,
3898  Message::AckType::Stats,
3899  Message::AckType::Processed | Message::AckType::Persisted,
3900  false);
3901  syncAckProcessing(timeout_, *publishStoreMessage,
3902  haSequenceNumber);
3903  }
3904  catch (const DisconnectedException&)
3905  {
3906  // -V565
3907  // Pass - it will get replayed upon reconnect
3908  }
3909  catch (...)
3910  {
3911  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId_));
3912  throw;
3913  }
3914  }
3915  return (std::string)commandId_;
3916  }
3917  else
3918  {
3919  Lock<Mutex> l(_lock);
3920  _message.reset();
3921  if (commandId_.empty())
3922  {
3923  _message.newCommandId();
3924  commandId_ = _message.getCommandId();
3925  }
3926  else
3927  {
3928  _message.setCommandId(commandId_.data(), commandId_.len());
3929  }
3930  _message.setCommandEnum(Message::Command::SOWDelete)
3931  .assignSubscriptionId(commandId_.data(), commandId_.len())
3932  .assignQueryID(commandId_.data(), commandId_.len())
3933  .setAckTypeEnum(Message::AckType::Processed |
3934  Message::AckType::Stats)
3935  .assignTopic(topic_.c_str(), topic_.length())
3936  .assignFilter(filter_.c_str(), filter_.length());
3937  _routes.addRoute(commandId_, messageHandler_,
3938  Message::AckType::Stats,
3939  Message::AckType::Processed,
3940  false);
3941  try
3942  {
3943  syncAckProcessing(timeout_, _message);
3944  }
3945  catch (...)
3946  {
3947  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId_));
3948  throw;
3949  }
3950  return (std::string)commandId_;
3951  }
3952  }
3953 
3954  std::string sowDeleteByData(const MessageHandler& messageHandler_,
3955  const std::string& topic_,
3956  const std::string& data_,
3957  long timeout_,
3958  Message::Field commandId_ = Message::Field())
3959  {
3960  if (_publishStore.isValid())
3961  {
3962  unsigned ackType = Message::AckType::Processed |
3963  Message::AckType::Stats |
3964  Message::AckType::Persisted;
3965  if (!publishStoreMessage)
3966  {
3967  publishStoreMessage = new Message();
3968  PerThreadMessageTracker::addMessageToCleanupList(publishStoreMessage);
3969  }
3970  publishStoreMessage->reset();
3971  if (commandId_.empty())
3972  {
3973  publishStoreMessage->newCommandId();
3974  commandId_ = publishStoreMessage->getCommandId();
3975  }
3976  else
3977  {
3978  publishStoreMessage->setCommandId(commandId_.data(), commandId_.len());
3979  }
3980  publishStoreMessage->setCommandEnum(Message::Command::SOWDelete)
3981  .assignSubscriptionId(commandId_.data(), commandId_.len())
3982  .assignQueryID(commandId_.data(), commandId_.len())
3983  .setAckTypeEnum(ackType)
3984  .assignTopic(topic_.c_str(), topic_.length())
3985  .assignData(data_.c_str(), data_.length());
3986  amps_uint64_t haSequenceNumber = _publishStore.store(*publishStoreMessage);
3987  char buf[AMPS_NUMBER_BUFFER_LEN];
3988  size_t pos = convertToCharArray(buf, haSequenceNumber);
3989  publishStoreMessage->assignSequence(buf + pos, AMPS_NUMBER_BUFFER_LEN - pos);
3990  {
3991  try
3992  {
3993  Lock<Mutex> l(_lock);
3994  _routes.addRoute(commandId_, messageHandler_,
3995  Message::AckType::Stats,
3996  Message::AckType::Processed | Message::AckType::Persisted,
3997  false);
3998  syncAckProcessing(timeout_, *publishStoreMessage,
3999  haSequenceNumber);
4000  }
4001  catch (const DisconnectedException&)
4002  {
4003  // -V565
4004  // Pass - it will get replayed upon reconnect
4005  }
4006  catch (...)
4007  {
4008  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId_));
4009  throw;
4010  }
4011  }
4012  return (std::string)commandId_;
4013  }
4014  else
4015  {
4016  Lock<Mutex> l(_lock);
4017  _message.reset();
4018  if (commandId_.empty())
4019  {
4020  _message.newCommandId();
4021  commandId_ = _message.getCommandId();
4022  }
4023  else
4024  {
4025  _message.setCommandId(commandId_.data(), commandId_.len());
4026  }
4027  _message.setCommandEnum(Message::Command::SOWDelete)
4028  .assignSubscriptionId(commandId_.data(), commandId_.len())
4029  .assignQueryID(commandId_.data(), commandId_.len())
4030  .setAckTypeEnum(Message::AckType::Processed |
4031  Message::AckType::Stats)
4032  .assignTopic(topic_.c_str(), topic_.length())
4033  .assignData(data_.c_str(), data_.length());
4034  _routes.addRoute(commandId_, messageHandler_,
4035  Message::AckType::Stats,
4036  Message::AckType::Processed,
4037  false);
4038  try
4039  {
4040  syncAckProcessing(timeout_, _message);
4041  }
4042  catch (...)
4043  {
4044  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId_));
4045  throw;
4046  }
4047  return (std::string)commandId_;
4048  }
4049  }
4050 
4051  std::string sowDeleteByKeys(const MessageHandler& messageHandler_,
4052  const std::string& topic_,
4053  const std::string& keys_,
4054  long timeout_,
4055  Message::Field commandId_ = Message::Field())
4056  {
4057  if (_publishStore.isValid())
4058  {
4059  unsigned ackType = Message::AckType::Processed |
4060  Message::AckType::Stats |
4061  Message::AckType::Persisted;
4062  if (!publishStoreMessage)
4063  {
4064  publishStoreMessage = new Message();
4065  PerThreadMessageTracker::addMessageToCleanupList(publishStoreMessage);
4066  }
4067  publishStoreMessage->reset();
4068  if (commandId_.empty())
4069  {
4070  publishStoreMessage->newCommandId();
4071  commandId_ = publishStoreMessage->getCommandId();
4072  }
4073  else
4074  {
4075  publishStoreMessage->setCommandId(commandId_.data(), commandId_.len());
4076  }
4077  publishStoreMessage->setCommandEnum(Message::Command::SOWDelete)
4078  .assignSubscriptionId(commandId_.data(), commandId_.len())
4079  .assignQueryID(commandId_.data(), commandId_.len())
4080  .setAckTypeEnum(ackType)
4081  .assignTopic(topic_.c_str(), topic_.length())
4082  .assignSowKeys(keys_.c_str(), keys_.length());
4083  amps_uint64_t haSequenceNumber = _publishStore.store(*publishStoreMessage);
4084  char buf[AMPS_NUMBER_BUFFER_LEN];
4085  size_t pos = convertToCharArray(buf, haSequenceNumber);
4086  publishStoreMessage->assignSequence(buf + pos, AMPS_NUMBER_BUFFER_LEN - pos);
4087  {
4088  try
4089  {
4090  Lock<Mutex> l(_lock);
4091  _routes.addRoute(commandId_, messageHandler_,
4092  Message::AckType::Stats,
4093  Message::AckType::Processed | Message::AckType::Persisted,
4094  false);
4095  syncAckProcessing(timeout_, *publishStoreMessage,
4096  haSequenceNumber);
4097  }
4098  catch (const DisconnectedException&)
4099  {
4100  // -V565
4101  // Pass - it will get replayed upon reconnect
4102  }
4103  catch (...)
4104  {
4105  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId_));
4106  throw;
4107  }
4108  }
4109  return (std::string)commandId_;
4110  }
4111  else
4112  {
4113  Lock<Mutex> l(_lock);
4114  _message.reset();
4115  if (commandId_.empty())
4116  {
4117  _message.newCommandId();
4118  commandId_ = _message.getCommandId();
4119  }
4120  else
4121  {
4122  _message.setCommandId(commandId_.data(), commandId_.len());
4123  }
4124  _message.setCommandEnum(Message::Command::SOWDelete)
4125  .assignSubscriptionId(commandId_.data(), commandId_.len())
4126  .assignQueryID(commandId_.data(), commandId_.len())
4127  .setAckTypeEnum(Message::AckType::Processed |
4128  Message::AckType::Stats)
4129  .assignTopic(topic_.c_str(), topic_.length())
4130  .assignSowKeys(keys_.c_str(), keys_.length());
4131  _routes.addRoute(commandId_, messageHandler_,
4132  Message::AckType::Stats,
4133  Message::AckType::Processed,
4134  false);
4135  try
4136  {
4137  syncAckProcessing(timeout_, _message);
4138  }
4139  catch (...)
4140  {
4141  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(commandId_));
4142  throw;
4143  }
4144  return (std::string)commandId_;
4145  }
4146  }
4147 
4148  void startTimer(void)
4149  {
4150  if (_serverVersion >= "5.3.2.0")
4151  {
4152  throw CommandException("The start_timer command is deprecated.");
4153  }
4154  Lock<Mutex> l(_lock);
4155  _message.reset();
4156  _message.setCommandEnum(Message::Command::StartTimer);
4157 
4158  _send(_message);
4159  }
4160 
4161  std::string stopTimer(MessageHandler messageHandler_)
4162  {
4163  if (_serverVersion >= "5.3.2.0")
4164  {
4165  throw CommandException("The stop_timer command is deprecated.");
4166  }
4167  return executeAsync(Command("stop_timer").addAckType("completed"), messageHandler_);
4168  }
4169 
4170  amps_handle getHandle(void)
4171  {
4172  return _client;
4173  }
4174 
4182  void setExceptionListener(const std::shared_ptr<const ExceptionListener>& pListener_)
4183  {
4184  _pExceptionListener = pListener_;
4185  _exceptionListener = _pExceptionListener.get();
4186  }
4187 
4188  void setExceptionListener(const ExceptionListener& listener_)
4189  {
4190  _exceptionListener = &listener_;
4191  }
4192 
4193  const ExceptionListener& getExceptionListener(void) const
4194  {
4195  return *_exceptionListener;
4196  }
4197 
4198  void setHeartbeat(unsigned heartbeatInterval_, unsigned readTimeout_)
4199  {
4200  if (readTimeout_ < heartbeatInterval_)
4201  {
4202  throw UsageException("The socket read timeout must be >= the heartbeat interval.");
4203  }
4204  Lock<Mutex> l(_lock);
4205  if (_heartbeatInterval != heartbeatInterval_ ||
4206  _readTimeout != readTimeout_)
4207  {
4208  _heartbeatInterval = heartbeatInterval_;
4209  _readTimeout = readTimeout_;
4210  _sendHeartbeat();
4211  }
4212  }
4213 
4214  void _sendHeartbeat(void)
4215  {
4216  if (_connected && _heartbeatInterval != 0)
4217  {
4218  std::ostringstream options;
4219  options << "start," << _heartbeatInterval;
4220  _beatMessage.setOptions(options.str());
4221 
4222  _heartbeatTimer.setTimeout(_heartbeatInterval * 1000.0);
4223  _heartbeatTimer.start();
4224  try
4225  {
4226  _sendWithoutRetry(_beatMessage);
4227  broadcastConnectionStateChanged(ConnectionStateListener::HeartbeatInitiated);
4228  }
4229  catch (ConnectionException& ex_)
4230  {
4231  // If we are disconnected when we attempt to send, that's OK;
4232  // we'll send this message after we re-connect (if we do).
4233  AMPS_UNHANDLED_EXCEPTION(ex_);
4234  }
4235  _beatMessage.setOptions("beat");
4236  }
4237  amps_result result = AMPS_E_OK;
4238  if (_readTimeout && _connected)
4239  {
4240  result = amps_client_set_read_timeout(_client, (int)_readTimeout);
4241  }
4242  if (result != AMPS_E_OK && result != AMPS_E_DISCONNECTED)
4243  {
4244  AMPSException::throwFor(_client, result);
4245  }
4246  }
4247 
4248  void addConnectionStateListener(ConnectionStateListener* listener_)
4249  {
4250  Lock<Mutex> lock(_lock);
4251  _connectionStateListeners.insert(listener_);
4252  }
4253 
4254  void removeConnectionStateListener(ConnectionStateListener* listener_)
4255  {
4256  Lock<Mutex> lock(_lock);
4257  _connectionStateListeners.erase(listener_);
4258  }
4259 
4260  void clearConnectionStateListeners()
4261  {
4262  Lock<Mutex> lock(_lock);
4263  _connectionStateListeners.clear();
4264  }
4265 
4266  void _registerHandler(Command& command_, Message::Field& cid_,
4267  MessageHandler& handler_, unsigned requestedAcks_,
4268  unsigned systemAddedAcks_, bool isSubscribe_)
4269  {
4270  Message message = command_.getMessage();
4271  Message::Command::Type commandType = message.getCommandEnum();
4272  Message::Field subid = message.getSubscriptionId();
4273  Message::Field qid = message.getQueryID();
4274  // If we have an id, we're good, even if it's an existing route
4275  bool added = qid.len() || subid.len() || cid_.len();
4276  bool cidIsQid = cid_ == qid;
4277  bool cidUnique = !cidIsQid && cid_.len() > 0 && cid_ != subid;
4278  int addedCount = 0;
4279  if (subid.len() > 0)
4280  {
4281  // This can replace a non-subscribe with a matching id
4282  // with a subscription but not another subscription.
4283  addedCount += _routes.addRoute(subid, handler_, requestedAcks_,
4284  systemAddedAcks_, isSubscribe_);
4285  if (!cidUnique
4286  && (commandType == Message::Command::Subscribe
4287  || commandType == Message::Command::DeltaSubscribe))
4288  {
4289  // We don't need to do anything else
4290  cid_ = subid;
4291  return;
4292  }
4293  }
4294  if (qid.len() > 0 && qid != subid
4295  && (commandType == Message::Command::SOW
4296  || commandType == Message::Command::SOWDelete
4297  || commandType == Message::Command::SOWAndSubscribe
4298  || commandType == Message::Command::SOWAndDeltaSubscribe))
4299  {
4300  while (_routes.hasRoute(qid))
4301  {
4302  message.newQueryId();
4303  if (cidIsQid)
4304  {
4305  cid_ = message.getQueryId();
4306  }
4307  qid = message.getQueryId();
4308  }
4309  if (addedCount == 0)
4310  {
4311  _routes.addRoute(qid, handler_, requestedAcks_,
4312  systemAddedAcks_, isSubscribe_);
4313  }
4314  else
4315  {
4316  void* data = NULL;
4317  {
4318  Unlock<Mutex> u(_lock);
4319  data = amps_invoke_copy_route_function(handler_.userData());
4320  }
4321  if (!data)
4322  {
4323  _routes.addRoute(qid, handler_, requestedAcks_,
4324  systemAddedAcks_, false);
4325  }
4326  else
4327  {
4328  _routes.addRoute(qid,
4329  MessageHandler(handler_.function(),
4330  data),
4331  requestedAcks_,
4332  systemAddedAcks_, false);
4333  }
4334  }
4335  ++addedCount;
4336  }
4337  if (cidUnique && requestedAcks_ & ~Message::AckType::Persisted)
4338  {
4339  while (_routes.hasRoute(cid_))
4340  {
4341  cid_ = message.newCommandId().getCommandId();
4342  }
4343  if (addedCount == 0)
4344  {
4345  _routes.addRoute(cid_, handler_, requestedAcks_,
4346  systemAddedAcks_, false);
4347  }
4348  else
4349  {
4350  void* data = NULL;
4351  {
4352  Unlock<Mutex> u(_lock);
4353  data = amps_invoke_copy_route_function(handler_.userData());
4354  }
4355  if (!data)
4356  {
4357  _routes.addRoute(cid_, handler_, requestedAcks_,
4358  systemAddedAcks_, false);
4359  }
4360  else
4361  {
4362  _routes.addRoute(cid_,
4363  MessageHandler(handler_.function(),
4364  data),
4365  requestedAcks_,
4366  systemAddedAcks_, false);
4367  }
4368  }
4369  }
4370  else if ((commandType == Message::Command::Publish ||
4371  commandType == Message::Command::DeltaPublish)
4372  && requestedAcks_ & ~Message::AckType::Persisted)
4373  {
4374  cid_ = command_.getMessage().newCommandId().getCommandId();
4375  _routes.addRoute(cid_, handler_, requestedAcks_,
4376  systemAddedAcks_, false);
4377  added = true;
4378  }
4379  if (!added)
4380  {
4381  throw UsageException("To use a messagehandler, you must also supply a command or subscription ID.");
4382  }
4383  }
4384 
4385  std::string executeAsyncNoLock(Command& command_, MessageHandler& handler_,
4386  bool isHASubscribe_ = true)
4387  {
4388  isHASubscribe_ &= (bool)_subscriptionManager;
4389  Message& message = command_.getMessage();
4390  unsigned systemAddedAcks = (handler_.isValid() || command_.hasProcessedAck()) ?
4391  Message::AckType::Processed : Message::AckType::None;
4392  unsigned requestedAcks = message.getAckTypeEnum();
4393  bool isPublishStore = _publishStore.isValid() && command_.needsSequenceNumber();
4394  Message::Command::Type commandType = message.getCommandEnum();
4395  if (commandType == Message::Command::SOW
4396  || commandType == Message::Command::SOWAndSubscribe
4397  || commandType == Message::Command::SOWAndDeltaSubscribe
4398  || commandType == Message::Command::StopTimer)
4399  {
4400  systemAddedAcks |= Message::AckType::Completed;
4401  }
4402  Message::Field cid = message.getCommandId();
4403  if (handler_.isValid() && cid.empty())
4404  {
4405  cid = message.newCommandId().getCommandId();
4406  }
4407  if (message.getBookmark().len() > 0)
4408  {
4409  if (command_.isSubscribe())
4410  {
4411  Message::Field bookmark = message.getBookmark();
4412  if (_bookmarkStore.isValid())
4413  {
4414  systemAddedAcks |= Message::AckType::Persisted;
4415  if (bookmark == AMPS_BOOKMARK_RECENT)
4416  {
4417  message.setBookmark(_bookmarkStore.getMostRecent(message.getSubscriptionId()));
4418  }
4419  else if (bookmark != AMPS_BOOKMARK_NOW &&
4420  bookmark != AMPS_BOOKMARK_EPOCH)
4421  {
4422  _bookmarkStore.log(message);
4423  if (!BookmarkRange::isRange(bookmark))
4424  {
4425  _bookmarkStore.discard(message);
4426  _bookmarkStore.persisted(message.getSubscriptionId(),
4427  bookmark);
4428  }
4429  }
4430  }
4431  else if (bookmark == AMPS_BOOKMARK_RECENT)
4432  {
4434  }
4435  }
4436  }
4437  if (isPublishStore)
4438  {
4439  systemAddedAcks |= Message::AckType::Persisted;
4440  }
4441  bool isSubscribe = command_.isSubscribe();
4442  if (handler_.isValid() && !isSubscribe)
4443  {
4444  _registerHandler(command_, cid, handler_,
4445  requestedAcks, systemAddedAcks, isSubscribe);
4446  }
4447  bool useSyncSend = cid.len() > 0 && command_.hasProcessedAck();
4448  if (isPublishStore)
4449  {
4450  amps_uint64_t haSequenceNumber = (amps_uint64_t)0;
4451  message.setAckTypeEnum(requestedAcks | systemAddedAcks);
4452  {
4453  Unlock<Mutex> u(_lock);
4454  haSequenceNumber = _publishStore.store(message);
4455  }
4456  message.setSequence(haSequenceNumber);
4457  try
4458  {
4459  if (useSyncSend)
4460  {
4461  syncAckProcessing((long)command_.getTimeout(), message,
4462  haSequenceNumber);
4463  }
4464  else
4465  {
4466  _send(message, haSequenceNumber);
4467  }
4468  }
4469  catch (const DisconnectedException&)
4470  {
4471  // -V565
4472  // Pass - message will get replayed when reconnected
4473  }
4474  catch (...)
4475  {
4476  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(cid));
4477  throw;
4478  }
4479  }
4480  else
4481  {
4482  if (isSubscribe)
4483  {
4484  const Message::Field& subId = message.getSubscriptionId();
4485  if (isHASubscribe_)
4486  {
4487  Unlock<Mutex> u(_lock);
4488  _subscriptionManager->subscribe(handler_,
4489  message.deepCopy(),
4490  requestedAcks);
4491  if (_badTimeToHASubscribe)
4492  {
4493  message.setAckTypeEnum(requestedAcks);
4494  return std::string(subId.data(), subId.len());
4495  }
4496  }
4497  if (handler_.isValid())
4498  {
4499  _registerHandler(command_, cid, handler_,
4500  requestedAcks, systemAddedAcks, isSubscribe);
4501  }
4502  message.setAckTypeEnum(requestedAcks | systemAddedAcks);
4503  try
4504  {
4505  if (useSyncSend)
4506  {
4507  syncAckProcessing((long)command_.getTimeout(), message,
4508  isHASubscribe_);
4509  }
4510  else
4511  {
4512  _send(message);
4513  }
4514  }
4515  catch (const DisconnectedException&)
4516  {
4517  if (!isHASubscribe_)
4518  {
4519  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(cid));
4520  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(subId));
4521  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(message.getQueryId()));
4522  message.setAckTypeEnum(requestedAcks);
4523  throw;
4524  }
4525  }
4526  catch (const TimedOutException&)
4527  {
4528  AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(cid));
4529  AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(subId));
4530  AMPS_CALL_EXCEPTION_WRAPPER(unsubscribeInternal(message.getQueryId()));
4531  throw;
4532  }
4533  catch (...)
4534  {
4535  if (isHASubscribe_)
4536  {
4537  // Have to unlock before calling into sub manager to avoid deadlock
4538  Unlock<Mutex> unlock(_lock);
4539  _subscriptionManager->unsubscribe(subId);
4540  }
4541  if (message.getQueryID().len() > 0)
4542  {
4543  _routes.removeRoute(message.getQueryID());
4544  }
4545  _routes.removeRoute(cid);
4546  _routes.removeRoute(subId);
4547  throw;
4548  }
4549  if (subId.len() > 0)
4550  {
4551  message.setAckTypeEnum(requestedAcks);
4552  return std::string(subId.data(), subId.len());
4553  }
4554  }
4555  else
4556  {
4557  message.setAckTypeEnum(requestedAcks | systemAddedAcks);
4558  try
4559  {
4560  if (useSyncSend)
4561  {
4562  syncAckProcessing((long)(command_.getTimeout()), message);
4563  }
4564  else
4565  {
4566  _send(message);
4567  }
4568  }
4569  catch (const DisconnectedException&)
4570  {
4571  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(cid));
4572  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(message.getQueryId()));
4573  message.setAckTypeEnum(requestedAcks);
4574  throw;
4575  }
4576  catch (...)
4577  {
4578  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(cid));
4579  AMPS_CALL_EXCEPTION_WRAPPER(_routes.removeRoute(message.getQueryId()));
4580  message.setAckTypeEnum(requestedAcks);
4581  throw;
4582  }
4583  }
4584  }
4585  message.setAckTypeEnum(requestedAcks);
4586  return cid;
4587  }
4588 
4589  MessageStream getEmptyMessageStream(void);
4590 
4591  std::string executeAsync(Command& command_, MessageHandler& handler_,
4592  bool isHASubscribe_ = true)
4593  {
4594  Lock<Mutex> lock(_lock);
4595  return executeAsyncNoLock(command_, handler_, isHASubscribe_);
4596  }
4597 
4598  // Queue Methods //
4599  void setAutoAck(bool isAutoAckEnabled_)
4600  {
4601  _isAutoAckEnabled = isAutoAckEnabled_;
4602  }
4603  bool getAutoAck(void) const
4604  {
4605  return _isAutoAckEnabled;
4606  }
4607  void setAckBatchSize(const unsigned batchSize_)
4608  {
4609  _ackBatchSize = batchSize_;
4610  if (!_queueAckTimeout)
4611  {
4612  _queueAckTimeout = AMPS_DEFAULT_QUEUE_ACK_TIMEOUT;
4613  amps_client_set_idle_time(_client, _queueAckTimeout);
4614  }
4615  }
4616  unsigned getAckBatchSize(void) const
4617  {
4618  return _ackBatchSize;
4619  }
4620  int getAckTimeout(void) const
4621  {
4622  return _queueAckTimeout;
4623  }
4624  void setAckTimeout(const int ackTimeout_)
4625  {
4626  amps_client_set_idle_time(_client, ackTimeout_);
4627  _queueAckTimeout = ackTimeout_;
4628  }
4629  size_t _ack(QueueBookmarks& queueBookmarks_)
4630  {
4631  if (queueBookmarks_._bookmarkCount)
4632  {
4633  if (!publishStoreMessage)
4634  {
4635  publishStoreMessage = new Message();
4636  PerThreadMessageTracker::addMessageToCleanupList(publishStoreMessage);
4637  }
4638  publishStoreMessage->reset();
4639  publishStoreMessage->setCommandEnum(Message::Command::SOWDelete)
4640  .setTopic(queueBookmarks_._topic)
4641  .setBookmark(queueBookmarks_._data)
4642  .setCommandId("AMPS-queue-ack");
4643  amps_uint64_t haSequenceNumber = 0;
4644  if (_publishStore.isValid())
4645  {
4646  haSequenceNumber = _publishStore.store(*publishStoreMessage);
4647  publishStoreMessage->setAckType("persisted")
4648  .setSequence(haSequenceNumber);
4649  queueBookmarks_._data.erase();
4650  queueBookmarks_._bookmarkCount = 0;
4651  }
4652  _send(*publishStoreMessage, haSequenceNumber);
4653  if (!_publishStore.isValid())
4654  {
4655  queueBookmarks_._data.erase();
4656  queueBookmarks_._bookmarkCount = 0;
4657  }
4658  return 1;
4659  }
4660  return 0;
4661  }
4662  void ack(const Field& topic_, const Field& bookmark_, const char* options_ = NULL)
4663  {
4664  if (_isAutoAckEnabled)
4665  {
4666  return;
4667  }
4668  _ack(topic_, bookmark_, options_);
4669  }
4670  void _ack(const Field& topic_, const Field& bookmark_, const char* options_ = NULL)
4671  {
4672  if (bookmark_.len() == 0)
4673  {
4674  return;
4675  }
4676  Lock<Mutex> lock(_lock);
4677  if (_ackBatchSize < 2 || options_ != NULL)
4678  {
4679  if (!publishStoreMessage)
4680  {
4681  publishStoreMessage = new Message();
4682  PerThreadMessageTracker::addMessageToCleanupList(publishStoreMessage);
4683  }
4684  publishStoreMessage->reset();
4685  publishStoreMessage->setCommandEnum(Message::Command::SOWDelete)
4686  .setCommandId("AMPS-queue-ack")
4687  .setTopic(topic_).setBookmark(bookmark_);
4688  if (options_)
4689  {
4690  publishStoreMessage->setOptions(options_);
4691  }
4692  amps_uint64_t haSequenceNumber = 0;
4693  if (_publishStore.isValid())
4694  {
4695  haSequenceNumber = _publishStore.store(*publishStoreMessage);
4696  publishStoreMessage->setAckType("persisted")
4697  .setSequence(haSequenceNumber);
4698  }
4699  _send(*publishStoreMessage, haSequenceNumber);
4700  return;
4701  }
4702  // have we acked anything for this hash
4703  topic_hash hash = CRC<0>::crcNoSSE(topic_.data(), topic_.len());
4704  TopicHashMap::iterator it = _topicHashMap.find(hash);
4705  if (it == _topicHashMap.end())
4706  {
4707  // add a new one to the map
4708 #ifdef AMPS_USE_EMPLACE
4709  it = _topicHashMap.emplace(TopicHashMap::value_type(hash, QueueBookmarks(topic_))).first;
4710 #else
4711  it = _topicHashMap.insert(TopicHashMap::value_type(hash, QueueBookmarks(topic_))).first;
4712 #endif
4713  }
4714  QueueBookmarks& queueBookmarks = it->second;
4715  if (queueBookmarks._data.length())
4716  {
4717  queueBookmarks._data.append(",");
4718  }
4719  else
4720  {
4721  queueBookmarks._oldestTime = amps_now();
4722  }
4723  queueBookmarks._data.append(bookmark_);
4724  if (++queueBookmarks._bookmarkCount >= _ackBatchSize)
4725  {
4726  _ack(queueBookmarks);
4727  }
4728  }
4729  void flushAcks(void)
4730  {
4731  size_t sendCount = 0;
4732  if (!_connected)
4733  {
4734  return;
4735  }
4736  else
4737  {
4738  Lock<Mutex> lock(_lock);
4739  typedef TopicHashMap::iterator iterator;
4740  for (iterator it = _topicHashMap.begin(), end = _topicHashMap.end(); it != end; ++it)
4741  {
4742  QueueBookmarks& queueBookmarks = it->second;
4743  sendCount += _ack(queueBookmarks);
4744  }
4745  }
4746  if (sendCount && _connected)
4747  {
4748  publishFlush(0, Message::AckType::Processed);
4749  }
4750  }
4751  // called when there's idle time, to see if we need to flush out any "acks"
4752  void checkQueueAcks(void)
4753  {
4754  if (!_topicHashMap.size())
4755  {
4756  return;
4757  }
4758  Lock<Mutex> lock(_lock);
4759  try
4760  {
4761  amps_uint64_t threshold = amps_now()
4762  - (amps_uint64_t)_queueAckTimeout;
4763  typedef TopicHashMap::iterator iterator;
4764  for (iterator it = _topicHashMap.begin(), end = _topicHashMap.end(); it != end; ++it)
4765  {
4766  QueueBookmarks& queueBookmarks = it->second;
4767  if (queueBookmarks._bookmarkCount && queueBookmarks._oldestTime < threshold)
4768  {
4769  _ack(queueBookmarks);
4770  }
4771  }
4772  }
4773  catch (std::exception& ex)
4774  {
4775  AMPS_UNHANDLED_EXCEPTION(ex);
4776  }
4777  }
4778 
4779  void deferredExecution(DeferredExecutionFunc func_, void* userData_)
4780  {
4781  Lock<Mutex> lock(_deferredExecutionLock);
4782 #ifdef AMPS_USE_EMPLACE
4783  _deferredExecutionList.emplace_back(
4784  DeferredExecutionRequest(func_, userData_));
4785 #else
4786  _deferredExecutionList.push_back(
4787  DeferredExecutionRequest(func_, userData_));
4788 #endif
4789  }
4790 
4791  inline void processDeferredExecutions(void)
4792  {
4793  if (_deferredExecutionList.size())
4794  {
4795  Lock<Mutex> lock(_deferredExecutionLock);
4796  DeferredExecutionList::iterator it = _deferredExecutionList.begin();
4797  DeferredExecutionList::iterator end = _deferredExecutionList.end();
4798  for (; it != end; ++it)
4799  {
4800  try
4801  {
4802  it->_func(it->_userData);
4803  }
4804  catch (...)
4805  {
4806  // -V565
4807  // Intentionally ignore errors
4808  }
4809  }
4810  _deferredExecutionList.clear();
4811  _routes.invalidateCache();
4812  _routeCache.invalidateCache();
4813  }
4814  }
4815 
4816  bool getRetryOnDisconnect(void) const
4817  {
4818  return _isRetryOnDisconnect;
4819  }
4820 
4821  void setRetryOnDisconnect(bool isRetryOnDisconnect_)
4822  {
4823  _isRetryOnDisconnect = isRetryOnDisconnect_;
4824  }
4825 
4826  void setDefaultMaxDepth(unsigned maxDepth_)
4827  {
4828  _defaultMaxDepth = maxDepth_;
4829  }
4830 
4831  unsigned getDefaultMaxDepth(void) const
4832  {
4833  return _defaultMaxDepth;
4834  }
4835 
4836  void setTransportFilterFunction(amps_transport_filter_function filter_,
4837  void* userData_)
4838  {
4839  amps_client_set_transport_filter_function(_client, filter_, userData_);
4840  }
4841 
4842  void setThreadCreatedCallback(amps_thread_created_callback callback_,
4843  void* userData_)
4844  {
4845  amps_client_set_thread_created_callback(_client, callback_, userData_);
4846  }
4847  }; // class ClientImpl
4922 
4924  {
4925  RefHandle<MessageStreamImpl> _body;
4926  public:
4931  class iterator
4932  {
4933  MessageStream* _pStream;
4934  Message _current;
4935  inline void advance(void);
4936 
4937  public:
4938  iterator() // end
4939  : _pStream(NULL)
4940  {;}
4941  iterator(MessageStream* pStream_)
4942  : _pStream(pStream_)
4943  {
4944  advance();
4945  }
4946 
4947  bool operator==(const iterator& rhs) const
4948  {
4949  return _pStream == rhs._pStream;
4950  }
4951  bool operator!=(const iterator& rhs) const
4952  {
4953  return _pStream != rhs._pStream;
4954  }
4955  void operator++(void)
4956  {
4957  advance();
4958  }
4959  Message operator*(void)
4960  {
4961  return _current;
4962  }
4963  Message* operator->(void)
4964  {
4965  return &_current;
4966  }
4967  };
4969  bool isValid() const
4970  {
4971  return _body.isValid();
4972  }
4973 
4977  {
4978  if (!_body.isValid())
4979  {
4980  throw UsageException("This MessageStream is not valid and cannot be iterated.");
4981  }
4982  return iterator(this);
4983  }
4986  // For non-SOW queries, the end is never reached.
4988  {
4989  return iterator();
4990  }
4991  inline MessageStream(void);
4992 
4998  MessageStream timeout(unsigned timeout_);
4999 
5003  MessageStream conflate(void);
5009  MessageStream maxDepth(unsigned maxDepth_);
5012  unsigned getMaxDepth(void) const;
5015  unsigned getDepth(void) const;
5016 
5017  private:
5018  inline MessageStream(const Client& client_);
5019  inline void setSOWOnly(const std::string& commandId_,
5020  const std::string& queryId_ = "");
5021  inline void setSubscription(const std::string& subId_,
5022  const std::string& commandId_ = "",
5023  const std::string& queryId_ = "");
5024  inline void setStatsOnly(const std::string& commandId_,
5025  const std::string& queryId_ = "");
5026  inline void setAcksOnly(const std::string& commandId_, unsigned acks_);
5027 
5028  inline operator MessageHandler(void);
5029 
5030  inline static MessageStream fromExistingHandler(const MessageHandler& handler);
5031 
5032  friend class Client;
5033 
5034  };
5035 
5055  class Client // -V553
5056  {
5057  protected:
5058  BorrowRefHandle<ClientImpl> _body;
5059  public:
5060  static const int DEFAULT_COMMAND_TIMEOUT = AMPS_DEFAULT_COMMAND_TIMEOUT;
5061  static const int DEFAULT_BATCH_SIZE = AMPS_DEFAULT_BATCH_SIZE;
5062  static const int DEFAULT_TOP_N = AMPS_DEFAULT_TOP_N;
5063 
5072  Client(const std::string& clientName = "")
5073  : _body(new ClientImpl(clientName), true)
5074  {;}
5075 
5076  Client(ClientImpl* existingClient)
5077  : _body(existingClient, true)
5078  {;}
5079 
5080  Client(ClientImpl* existingClient, bool isRef)
5081  : _body(existingClient, isRef)
5082  {;}
5083 
5084  Client(const Client& rhs) : _body(rhs._body) {;}
5085  virtual ~Client(void) {;}
5086 
5087  Client& operator=(const Client& rhs)
5088  {
5089  _body = rhs._body;
5090  return *this;
5091  }
5092 
5093  bool isValid()
5094  {
5095  return _body.isValid();
5096  }
5097 
5110  void setName(const std::string& name)
5111  {
5112  _body.get().setName(name);
5113  }
5114 
5117  const std::string& getName() const
5118  {
5119  return _body.get().getName();
5120  }
5121 
5125  const std::string& getNameHash() const
5126  {
5127  return _body.get().getNameHash();
5128  }
5129 
5133  const amps_uint64_t getNameHashValue() const
5134  {
5135  return _body.get().getNameHashValue();
5136  }
5137 
5144  void setLogonCorrelationData(const std::string& logonCorrelationData_)
5145  {
5146  _body.get().setLogonCorrelationData(logonCorrelationData_);
5147  }
5148 
5151  const std::string& getLogonCorrelationData() const
5152  {
5153  return _body.get().getLogonCorrelationData();
5154  }
5155 
5164  size_t getServerVersion() const
5165  {
5166  return _body.get().getServerVersion();
5167  }
5168 
5175  VersionInfo getServerVersionInfo() const
5176  {
5177  return _body.get().getServerVersionInfo();
5178  }
5179 
5189  static size_t convertVersionToNumber(const std::string& version_)
5190  {
5191  return AMPS::convertVersionToNumber(version_.c_str(), version_.length());
5192  }
5193 
5204  static size_t convertVersionToNumber(const char* data_, size_t len_)
5205  {
5206  return AMPS::convertVersionToNumber(data_, len_);
5207  }
5208 
5211  const std::string& getURI() const
5212  {
5213  return _body.get().getURI();
5214  }
5215 
5222 
5224 
5235  void connect(const std::string& uri)
5236  {
5237  _body.get().connect(uri);
5238  }
5239 
5242  void disconnect()
5243  {
5244  _body.get().disconnect();
5245  }
5246 
5260  void send(const Message& message)
5261  {
5262  _body.get().send(message);
5263  }
5264 
5273  void addMessageHandler(const Field& commandId_,
5274  const AMPS::MessageHandler& messageHandler_,
5275  unsigned requestedAcks_, bool isSubscribe_)
5276  {
5277  _body.get().addMessageHandler(commandId_, messageHandler_,
5278  requestedAcks_, isSubscribe_);
5279  }
5280 
5284  bool removeMessageHandler(const Field& commandId_)
5285  {
5286  return _body.get().removeMessageHandler(commandId_);
5287  }
5288 
5312  std::string send(const MessageHandler& messageHandler, Message& message, int timeout = 0)
5313  {
5314  return _body.get().send(messageHandler, message, timeout);
5315  }
5316 
5326  void setDisconnectHandler(const DisconnectHandler& disconnectHandler)
5327  {
5328  _body.get().setDisconnectHandler(disconnectHandler);
5329  }
5330 
5334  DisconnectHandler getDisconnectHandler(void) const
5335  {
5336  return _body.get().getDisconnectHandler();
5337  }
5338 
5343  virtual ConnectionInfo getConnectionInfo() const
5344  {
5345  return _body.get().getConnectionInfo();
5346  }
5347 
5356  void setBookmarkStore(const BookmarkStore& bookmarkStore_)
5357  {
5358  _body.get().setBookmarkStore(bookmarkStore_);
5359  }
5360 
5365  {
5366  return _body.get().getBookmarkStore();
5367  }
5368 
5373  {
5374  return _body.get().getSubscriptionManager();
5375  }
5376 
5384  void setSubscriptionManager(SubscriptionManager* subscriptionManager_)
5385  {
5386  _body.get().setSubscriptionManager(subscriptionManager_);
5387  }
5388 
5408  void setPublishStore(const Store& publishStore_)
5409  {
5410  _body.get().setPublishStore(publishStore_);
5411  }
5412 
5417  {
5418  return _body.get().getPublishStore();
5419  }
5420 
5424  void setDuplicateMessageHandler(const MessageHandler& duplicateMessageHandler_)
5425  {
5426  _body.get().setGlobalCommandTypeMessageHandler(ClientImpl::GlobalCommandTypeHandlers::DuplicateMessage,
5427  duplicateMessageHandler_);
5428  }
5429 
5440  {
5441  return _body.get().getDuplicateMessageHandler();
5442  }
5443 
5454  {
5455  _body.get().setFailedWriteHandler(handler_);
5456  }
5457 
5462  {
5463  return _body.get().getFailedWriteHandler();
5464  }
5465 
5466 
5484  amps_uint64_t publish(const std::string& topic_, const std::string& data_)
5485  {
5486  return _body.get().publish(topic_.c_str(), topic_.length(),
5487  data_.c_str(), data_.length());
5488  }
5489 
5509  amps_uint64_t publish(const char* topic_, size_t topicLength_,
5510  const char* data_, size_t dataLength_)
5511  {
5512  return _body.get().publish(topic_, topicLength_, data_, dataLength_);
5513  }
5514 
5533  amps_uint64_t publish(const std::string& topic_, const std::string& data_,
5534  unsigned long expiration_)
5535  {
5536  return _body.get().publish(topic_.c_str(), topic_.length(),
5537  data_.c_str(), data_.length(), expiration_);
5538  }
5539 
5560  amps_uint64_t publish(const char* topic_, size_t topicLength_,
5561  const char* data_, size_t dataLength_,
5562  unsigned long expiration_)
5563  {
5564  return _body.get().publish(topic_, topicLength_,
5565  data_, dataLength_, expiration_);
5566  }
5567 
5606  void publishFlush(long timeout_ = 0, unsigned ackType_ = Message::AckType::Processed)
5607  {
5608  _body.get().publishFlush(timeout_, ackType_);
5609  }
5610 
5611 
5627  amps_uint64_t deltaPublish(const std::string& topic_, const std::string& data_)
5628  {
5629  return _body.get().deltaPublish(topic_.c_str(), topic_.length(),
5630  data_.c_str(), data_.length());
5631  }
5632 
5650  amps_uint64_t deltaPublish(const char* topic_, size_t topicLength_,
5651  const char* data_, size_t dataLength_)
5652  {
5653  return _body.get().deltaPublish(topic_, topicLength_,
5654  data_, dataLength_);
5655  }
5656 
5673  amps_uint64_t deltaPublish(const std::string& topic_, const std::string& data_,
5674  unsigned long expiration_)
5675  {
5676  return _body.get().deltaPublish(topic_.c_str(), topic_.length(),
5677  data_.c_str(), data_.length(),
5678  expiration_);
5679  }
5680 
5699  amps_uint64_t deltaPublish(const char* topic_, size_t topicLength_,
5700  const char* data_, size_t dataLength_,
5701  unsigned long expiration_)
5702  {
5703  return _body.get().deltaPublish(topic_, topicLength_,
5704  data_, dataLength_, expiration_);
5705  }
5706 
5722  std::string logon(int timeout_ = 0,
5723  Authenticator& authenticator_ = DefaultAuthenticator::instance(),
5724  const char* options_ = NULL)
5725  {
5726  return _body.get().logon(timeout_, authenticator_, options_);
5727  }
5741  std::string logon(const char* options_, int timeout_ = 0)
5742  {
5743  return _body.get().logon(timeout_, DefaultAuthenticator::instance(),
5744  options_);
5745  }
5746 
5760  std::string logon(const std::string& options_, int timeout_ = 0)
5761  {
5762  return _body.get().logon(timeout_, DefaultAuthenticator::instance(),
5763  options_.c_str());
5764  }
5765 
5785  std::string subscribe(const MessageHandler& messageHandler_,
5786  const std::string& topic_,
5787  long timeout_ = 0,
5788  const std::string& filter_ = "",
5789  const std::string& options_ = "",
5790  const std::string& subId_ = "")
5791  {
5792  return _body.get().subscribe(messageHandler_, topic_, timeout_,
5793  filter_, "", options_, subId_);
5794  }
5795 
5811  MessageStream subscribe(const std::string& topic_,
5812  long timeout_ = 0, const std::string& filter_ = "",
5813  const std::string& options_ = "",
5814  const std::string& subId_ = "")
5815  {
5816  MessageStream result(*this);
5817  if (_body.get().getDefaultMaxDepth())
5818  {
5819  result.maxDepth(_body.get().getDefaultMaxDepth());
5820  }
5821  result.setSubscription(_body.get().subscribe(
5822  result.operator MessageHandler(),
5823  topic_, timeout_, filter_, "",
5824  options_, subId_, false));
5825  return result;
5826  }
5827 
5843  MessageStream subscribe(const char* topic_,
5844  long timeout_ = 0, const std::string& filter_ = "",
5845  const std::string& options_ = "",
5846  const std::string& subId_ = "")
5847  {
5848  MessageStream result(*this);
5849  if (_body.get().getDefaultMaxDepth())
5850  {
5851  result.maxDepth(_body.get().getDefaultMaxDepth());
5852  }
5853  result.setSubscription(_body.get().subscribe(
5854  result.operator MessageHandler(),
5855  topic_, timeout_, filter_, "",
5856  options_, subId_, false));
5857  return result;
5858  }
5859 
5872  std::string deltaSubscribe(const MessageHandler& messageHandler_,
5873  const std::string& topic_,
5874  long timeout_,
5875  const std::string& filter_ = "",
5876  const std::string& options_ = "",
5877  const std::string& subId_ = "")
5878  {
5879  return _body.get().deltaSubscribe(messageHandler_, topic_, timeout_,
5880  filter_, "", options_, subId_);
5881  }
5890  MessageStream deltaSubscribe(const std::string& topic_,
5891  long timeout_, const std::string& filter_ = "",
5892  const std::string& options_ = "",
5893  const std::string& subId_ = "")
5894  {
5895  MessageStream result(*this);
5896  if (_body.get().getDefaultMaxDepth())
5897  {
5898  result.maxDepth(_body.get().getDefaultMaxDepth());
5899  }
5900  result.setSubscription(_body.get().deltaSubscribe(
5901  result.operator MessageHandler(),
5902  topic_, timeout_, filter_, "",
5903  options_, subId_, false));
5904  return result;
5905  }
5906 
5908  MessageStream deltaSubscribe(const char* topic_,
5909  long timeout_, const std::string& filter_ = "",
5910  const std::string& options_ = "",
5911  const std::string& subId_ = "")
5912  {
5913  MessageStream result(*this);
5914  if (_body.get().getDefaultMaxDepth())
5915  {
5916  result.maxDepth(_body.get().getDefaultMaxDepth());
5917  }
5918  result.setSubscription(_body.get().deltaSubscribe(
5919  result.operator MessageHandler(),
5920  topic_, timeout_, filter_, "",
5921  options_, subId_, false));
5922  return result;
5923  }
5924 
5950  std::string bookmarkSubscribe(const MessageHandler& messageHandler_,
5951  const std::string& topic_,
5952  long timeout_,
5953  const std::string& bookmark_,
5954  const std::string& filter_ = "",
5955  const std::string& options_ = "",
5956  const std::string& subId_ = "")
5957  {
5958  return _body.get().subscribe(messageHandler_, topic_, timeout_,
5959  filter_, bookmark_, options_, subId_);
5960  }
5978  MessageStream bookmarkSubscribe(const std::string& topic_,
5979  long timeout_,
5980  const std::string& bookmark_,
5981  const std::string& filter_ = "",
5982  const std::string& options_ = "",
5983  const std::string& subId_ = "")
5984  {
5985  MessageStream result(*this);
5986  if (_body.get().getDefaultMaxDepth())
5987  {
5988  result.maxDepth(_body.get().getDefaultMaxDepth());
5989  }
5990  result.setSubscription(_body.get().subscribe(
5991  result.operator MessageHandler(),
5992  topic_, timeout_, filter_,
5993  bookmark_, options_,
5994  subId_, false));
5995  return result;
5996  }
5997 
5999  MessageStream bookmarkSubscribe(const char* topic_,
6000  long timeout_,
6001  const std::string& bookmark_,
6002  const std::string& filter_ = "",
6003  const std::string& options_ = "",
6004  const std::string& subId_ = "")
6005  {
6006  MessageStream result(*this);
6007  if (_body.get().getDefaultMaxDepth())
6008  {
6009  result.maxDepth(_body.get().getDefaultMaxDepth());
6010  }
6011  result.setSubscription(_body.get().subscribe(
6012  result.operator MessageHandler(),
6013  topic_, timeout_, filter_,
6014  bookmark_, options_,
6015  subId_, false));
6016  return result;
6017  }
6018 
6027  void unsubscribe(const std::string& commandId)
6028  {
6029  return _body.get().unsubscribe(commandId);
6030  }
6031 
6040  {
6041  return _body.get().unsubscribe();
6042  }
6043 
6044 
6074  std::string sow(const MessageHandler& messageHandler_,
6075  const std::string& topic_,
6076  const std::string& filter_ = "",
6077  const std::string& orderBy_ = "",
6078  const std::string& bookmark_ = "",
6079  int batchSize_ = DEFAULT_BATCH_SIZE,
6080  int topN_ = DEFAULT_TOP_N,
6081  const std::string& options_ = "",
6082  long timeout_ = DEFAULT_COMMAND_TIMEOUT)
6083  {
6084  return _body.get().sow(messageHandler_, topic_, filter_, orderBy_,
6085  bookmark_, batchSize_, topN_, options_,
6086  timeout_);
6087  }
6112  MessageStream sow(const std::string& topic_,
6113  const std::string& filter_ = "",
6114  const std::string& orderBy_ = "",
6115  const std::string& bookmark_ = "",
6116  int batchSize_ = DEFAULT_BATCH_SIZE,
6117  int topN_ = DEFAULT_TOP_N,
6118  const std::string& options_ = "",
6119  long timeout_ = DEFAULT_COMMAND_TIMEOUT)
6120  {
6121  MessageStream result(*this);
6122  if (_body.get().getDefaultMaxDepth())
6123  {
6124  result.maxDepth(_body.get().getDefaultMaxDepth());
6125  }
6126  result.setSOWOnly(sow(result.operator MessageHandler(), topic_, filter_, orderBy_, bookmark_, batchSize_, topN_, options_, timeout_));
6127  return result;
6128  }
6129 
6131  MessageStream sow(const char* topic_,
6132  const std::string& filter_ = "",
6133  const std::string& orderBy_ = "",
6134  const std::string& bookmark_ = "",
6135  int batchSize_ = DEFAULT_BATCH_SIZE,
6136  int topN_ = DEFAULT_TOP_N,
6137  const std::string& options_ = "",
6138  long timeout_ = DEFAULT_COMMAND_TIMEOUT)
6139  {
6140  MessageStream result(*this);
6141  if (_body.get().getDefaultMaxDepth())
6142  {
6143  result.maxDepth(_body.get().getDefaultMaxDepth());
6144  }
6145  result.setSOWOnly(sow(result.operator MessageHandler(), topic_, filter_, orderBy_, bookmark_, batchSize_, topN_, options_, timeout_));
6146  return result;
6147  }
6170  std::string sow(const MessageHandler& messageHandler_,
6171  const std::string& topic_,
6172  long timeout_,
6173  const std::string& filter_ = "",
6174  int batchSize_ = DEFAULT_BATCH_SIZE,
6175  int topN_ = DEFAULT_TOP_N)
6176  {
6177  return _body.get().sow(messageHandler_, topic_, timeout_, filter_,
6178  batchSize_, topN_);
6179  }
6202  std::string sowAndSubscribe(const MessageHandler& messageHandler_,
6203  const std::string& topic_,
6204  long timeout_,
6205  const std::string& filter_ = "",
6206  int batchSize_ = DEFAULT_BATCH_SIZE,
6207  bool oofEnabled_ = false,
6208  int topN_ = DEFAULT_TOP_N)
6209  {
6210  return _body.get().sowAndSubscribe(messageHandler_, topic_, timeout_,
6211  filter_, batchSize_, oofEnabled_,
6212  topN_);
6213  }
6214 
6234  MessageStream sowAndSubscribe(const std::string& topic_,
6235  long timeout_,
6236  const std::string& filter_ = "",
6237  int batchSize_ = DEFAULT_BATCH_SIZE,
6238  bool oofEnabled_ = false,
6239  int topN_ = DEFAULT_TOP_N)
6240  {
6241  MessageStream result(*this);
6242  if (_body.get().getDefaultMaxDepth())
6243  {
6244  result.maxDepth(_body.get().getDefaultMaxDepth());
6245  }
6246  result.setSubscription(_body.get().sowAndSubscribe(
6247  result.operator MessageHandler(),
6248  topic_, timeout_, filter_,
6249  batchSize_, oofEnabled_,
6250  topN_, false));
6251  return result;
6252  }
6272  MessageStream sowAndSubscribe(const char* topic_,
6273  long timeout_,
6274  const std::string& filter_ = "",
6275  int batchSize_ = DEFAULT_BATCH_SIZE,
6276  bool oofEnabled_ = false,
6277  int topN_ = DEFAULT_TOP_N)
6278  {
6279  MessageStream result(*this);
6280  if (_body.get().getDefaultMaxDepth())
6281  {
6282  result.maxDepth(_body.get().getDefaultMaxDepth());
6283  }
6284  result.setSubscription(_body.get().sowAndSubscribe(
6285  result.operator MessageHandler(),
6286  topic_, timeout_, filter_,
6287  batchSize_, oofEnabled_,
6288  topN_, false));
6289  return result;
6290  }
6291 
6292 
6320  std::string sowAndSubscribe(const MessageHandler& messageHandler_,
6321  const std::string& topic_,
6322  const std::string& filter_ = "",
6323  const std::string& orderBy_ = "",
6324  const std::string& bookmark_ = "",
6325  int batchSize_ = DEFAULT_BATCH_SIZE,
6326  int topN_ = DEFAULT_TOP_N,
6327  const std::string& options_ = "",
6328  long timeout_ = DEFAULT_COMMAND_TIMEOUT)
6329  {
6330  return _body.get().sowAndSubscribe(messageHandler_, topic_, filter_,
6331  orderBy_, bookmark_, batchSize_,
6332  topN_, options_, timeout_);
6333  }
6334 
6359  MessageStream sowAndSubscribe(const std::string& topic_,
6360  const std::string& filter_ = "",
6361  const std::string& orderBy_ = "",
6362  const std::string& bookmark_ = "",
6363  int batchSize_ = DEFAULT_BATCH_SIZE,
6364  int topN_ = DEFAULT_TOP_N,
6365  const std::string& options_ = "",
6366  long timeout_ = DEFAULT_COMMAND_TIMEOUT)
6367  {
6368  MessageStream result(*this);
6369  if (_body.get().getDefaultMaxDepth())
6370  {
6371  result.maxDepth(_body.get().getDefaultMaxDepth());
6372  }
6373  result.setSubscription(_body.get().sowAndSubscribe(
6374  result.operator MessageHandler(),
6375  topic_, filter_, orderBy_,
6376  bookmark_, batchSize_, topN_,
6377  options_, timeout_, false));
6378  return result;
6379  }
6380 
6382  MessageStream sowAndSubscribe(const char* topic_,
6383  const std::string& filter_ = "",
6384  const std::string& orderBy_ = "",
6385  const std::string& bookmark_ = "",
6386  int batchSize_ = DEFAULT_BATCH_SIZE,
6387  int topN_ = DEFAULT_TOP_N,
6388  const std::string& options_ = "",
6389  long timeout_ = DEFAULT_COMMAND_TIMEOUT)
6390  {
6391  MessageStream result(*this);
6392  if (_body.get().getDefaultMaxDepth())
6393  {
6394  result.maxDepth(_body.get().getDefaultMaxDepth());
6395  }
6396  result.setSubscription(_body.get().sowAndSubscribe(
6397  result.operator MessageHandler(),
6398  topic_, filter_, orderBy_,
6399  bookmark_, batchSize_, topN_,
6400  options_, timeout_, false));
6401  return result;
6402  }
6403 
6428  std::string sowAndDeltaSubscribe(const MessageHandler& messageHandler_,
6429  const std::string& topic_,
6430  const std::string& filter_ = "",
6431  const std::string& orderBy_ = "",
6432  int batchSize_ = DEFAULT_BATCH_SIZE,
6433  int topN_ = DEFAULT_TOP_N,
6434  const std::string& options_ = "",
6435  long timeout_ = DEFAULT_COMMAND_TIMEOUT)
6436  {
6437  return _body.get().sowAndDeltaSubscribe(messageHandler_, topic_,
6438  filter_, orderBy_, batchSize_,
6439  topN_, options_, timeout_);
6440  }
6461  MessageStream sowAndDeltaSubscribe(const std::string& topic_,
6462  const std::string& filter_ = "",
6463  const std::string& orderBy_ = "",
6464  int batchSize_ = DEFAULT_BATCH_SIZE,
6465  int topN_ = DEFAULT_TOP_N,
6466  const std::string& options_ = "",
6467  long timeout_ = DEFAULT_COMMAND_TIMEOUT)
6468  {
6469  MessageStream result(*this);
6470  if (_body.get().getDefaultMaxDepth())
6471  {
6472  result.maxDepth(_body.get().getDefaultMaxDepth());
6473  }
6474  result.setSubscription(sowAndDeltaSubscribe(result.operator MessageHandler(), topic_, filter_, orderBy_, batchSize_, topN_, options_, timeout_));
6475  result.setSubscription(_body.get().sowAndDeltaSubscribe(
6476  result.operator MessageHandler(),
6477  topic_, filter_, orderBy_,
6478  batchSize_, topN_, options_,
6479  timeout_, false));
6480  return result;
6481  }
6482 
6485  const std::string& filter_ = "",
6486  const std::string& orderBy_ = "",
6487  int batchSize_ = DEFAULT_BATCH_SIZE,
6488  int topN_ = DEFAULT_TOP_N,
6489  const std::string& options_ = "",
6490  long timeout_ = DEFAULT_COMMAND_TIMEOUT)
6491  {
6492  MessageStream result(*this);
6493  if (_body.get().getDefaultMaxDepth())
6494  {
6495  result.maxDepth(_body.get().getDefaultMaxDepth());
6496  }
6497  result.setSubscription(_body.get().sowAndDeltaSubscribe(
6498  result.operator MessageHandler(),
6499  topic_, filter_, orderBy_,
6500  batchSize_, topN_, options_,
6501  timeout_, false));
6502  return result;
6503  }
6504 
6529  std::string sowAndDeltaSubscribe(const MessageHandler& messageHandler_,
6530  const std::string& topic_,
6531  long timeout_,
6532  const std::string& filter_ = "",
6533  int batchSize_ = DEFAULT_BATCH_SIZE,
6534  bool oofEnabled_ = false,
6535  bool sendEmpties_ = false,
6536  int topN_ = DEFAULT_TOP_N)
6537  {
6538  return _body.get().sowAndDeltaSubscribe(messageHandler_, topic_,
6539  timeout_, filter_, batchSize_,
6540  oofEnabled_, sendEmpties_,
6541  topN_);
6542  }
6543 
6565  MessageStream sowAndDeltaSubscribe(const std::string& topic_,
6566  long timeout_,
6567  const std::string& filter_ = "",
6568  int batchSize_ = DEFAULT_BATCH_SIZE,
6569  bool oofEnabled_ = false,
6570  bool sendEmpties_ = false,
6571  int topN_ = DEFAULT_TOP_N)
6572  {
6573  MessageStream result(*this);
6574  if (_body.get().getDefaultMaxDepth())
6575  {
6576  result.maxDepth(_body.get().getDefaultMaxDepth());
6577  }
6578  result.setSubscription(_body.get().sowAndDeltaSubscribe(
6579  result.operator MessageHandler(),
6580  topic_, timeout_, filter_,
6581  batchSize_, oofEnabled_,
6582  sendEmpties_, topN_, false));
6583  return result;
6584  }
6607  long timeout_,
6608  const std::string& filter_ = "",
6609  int batchSize_ = DEFAULT_BATCH_SIZE,
6610  bool oofEnabled_ = false,
6611  bool sendEmpties_ = false,
6612  int topN_ = DEFAULT_TOP_N)
6613  {
6614  MessageStream result(*this);
6615  if (_body.get().getDefaultMaxDepth())
6616  {
6617  result.maxDepth(_body.get().getDefaultMaxDepth());
6618  }
6619  result.setSubscription(_body.get().sowAndDeltaSubscribe(
6620  result.operator MessageHandler(),
6621  topic_, timeout_, filter_,
6622  batchSize_, oofEnabled_,
6623  sendEmpties_, topN_, false));
6624  return result;
6625  }
6645  std::string sowDelete(const MessageHandler& messageHandler,
6646  const std::string& topic,
6647  const std::string& filter,
6648  long timeout)
6649  {
6650  return _body.get().sowDelete(messageHandler, topic, filter, timeout);
6651  }
6668  Message sowDelete(const std::string& topic, const std::string& filter,
6669  long timeout = 0)
6670  {
6671  MessageStream stream(*this);
6672  char buf[Message::IdentifierLength + 1];
6673  buf[Message::IdentifierLength] = 0;
6674  AMPS_snprintf(buf, Message::IdentifierLength + 1, "%lx", MessageImpl::newId());
6675  Field cid(buf);
6676  try
6677  {
6678  stream.setStatsOnly(cid);
6679  _body.get().sowDelete(stream.operator MessageHandler(), topic, filter, timeout, cid);
6680  return *(stream.begin());
6681  }
6682  catch (const DisconnectedException&)
6683  {
6684  removeMessageHandler(cid);
6685  throw;
6686  }
6687  }
6688 
6693  void startTimer()
6694  {
6695  _body.get().startTimer();
6696  }
6697 
6704  std::string stopTimer(const MessageHandler& messageHandler)
6705  {
6706  return _body.get().stopTimer(messageHandler);
6707  }
6708 
6730  std::string sowDeleteByKeys(const MessageHandler& messageHandler_,
6731  const std::string& topic_,
6732  const std::string& keys_,
6733  long timeout_ = 0)
6734  {
6735  return _body.get().sowDeleteByKeys(messageHandler_, topic_, keys_, timeout_);
6736  }
6757  Message sowDeleteByKeys(const std::string& topic_, const std::string& keys_,
6758  long timeout_ = 0)
6759  {
6760  MessageStream stream(*this);
6761  char buf[Message::IdentifierLength + 1];
6762  buf[Message::IdentifierLength] = 0;
6763  AMPS_snprintf(buf, Message::IdentifierLength + 1, "%lx", MessageImpl::newId());
6764  Field cid(buf);
6765  try
6766  {
6767  stream.setStatsOnly(cid);
6768  _body.get().sowDeleteByKeys(stream.operator MessageHandler(), topic_, keys_, timeout_, cid);
6769  return *(stream.begin());
6770  }
6771  catch (const DisconnectedException&)
6772  {
6773  removeMessageHandler(cid);
6774  throw;
6775  }
6776  }
6777 
6792  std::string sowDeleteByData(const MessageHandler& messageHandler_,
6793  const std::string& topic_, const std::string& data_,
6794  long timeout_ = 0)
6795  {
6796  return _body.get().sowDeleteByData(messageHandler_, topic_, data_, timeout_);
6797  }
6798 
6813  Message sowDeleteByData(const std::string& topic_, const std::string& data_,
6814  long timeout_ = 0)
6815  {
6816  MessageStream stream(*this);
6817  char buf[Message::IdentifierLength + 1];
6818  buf[Message::IdentifierLength] = 0;
6819  AMPS_snprintf(buf, Message::IdentifierLength + 1, "%lx", MessageImpl::newId());
6820  Field cid(buf);
6821  try
6822  {
6823  stream.setStatsOnly(cid);
6824  _body.get().sowDeleteByData(stream.operator MessageHandler(), topic_, data_, timeout_, cid);
6825  return *(stream.begin());
6826  }
6827  catch (const DisconnectedException&)
6828  {
6829  removeMessageHandler(cid);
6830  throw;
6831  }
6832  }
6833 
6838  {
6839  return _body.get().getHandle();
6840  }
6841 
6850  void setExceptionListener(const std::shared_ptr<const ExceptionListener>& pListener_)
6851  {
6852  _body.get().setExceptionListener(pListener_);
6853  }
6854 
6864  {
6865  _body.get().setExceptionListener(listener_);
6866  }
6867 
6871  {
6872  return _body.get().getExceptionListener();
6873  }
6874 
6882  // type of message) from the server for the specified interval (plus a grace period),
6896  void setHeartbeat(unsigned heartbeatTime_, unsigned readTimeout_)
6897  {
6898  _body.get().setHeartbeat(heartbeatTime_, readTimeout_);
6899  }
6900 
6908  // type of message) from the server for the specified interval (plus a grace period),
6920  void setHeartbeat(unsigned heartbeatTime_)
6921  {
6922  _body.get().setHeartbeat(heartbeatTime_, 2 * heartbeatTime_);
6923  }
6924 
6927  {
6928  setLastChanceMessageHandler(messageHandler);
6929  }
6930 
6934  {
6935  _body.get().setGlobalCommandTypeMessageHandler(ClientImpl::GlobalCommandTypeHandlers::LastChance,
6936  messageHandler);
6937  }
6938 
6959  void setGlobalCommandTypeMessageHandler(const std::string& command_, const MessageHandler& handler_)
6960  {
6961  _body.get().setGlobalCommandTypeMessageHandler(command_, handler_);
6962  }
6963 
6984  void setGlobalCommandTypeMessageHandler(const Message::Command::Type command_, const MessageHandler& handler_)
6985  {
6986  _body.get().setGlobalCommandTypeMessageHandler(command_, handler_);
6987  }
6988 
6994  static const char* BOOKMARK_NOW()
6995  {
6996  return AMPS_BOOKMARK_NOW;
6997  }
7003  static const char* NOW()
7004  {
7005  return AMPS_BOOKMARK_NOW;
7006  }
7007 
7013  static const char* BOOKMARK_EPOCH()
7014  {
7015  return AMPS_BOOKMARK_EPOCH;
7016  }
7017 
7023  static const char* EPOCH()
7024  {
7025  return AMPS_BOOKMARK_EPOCH;
7026  }
7027 
7034  static const char* BOOKMARK_MOST_RECENT()
7035  {
7036  return AMPS_BOOKMARK_RECENT;
7037  }
7038 
7045  static const char* MOST_RECENT()
7046  {
7047  return AMPS_BOOKMARK_RECENT;
7048  }
7049 
7056  static const char* BOOKMARK_RECENT()
7057  {
7058  return AMPS_BOOKMARK_RECENT;
7059  }
7060 
7061 
7068  {
7069  _body.get().addConnectionStateListener(listener);
7070  }
7071 
7076  {
7077  _body.get().removeConnectionStateListener(listener);
7078  }
7079 
7083  {
7084  _body.get().clearConnectionStateListeners();
7085  }
7086 
7112  std::string executeAsync(Command& command_, MessageHandler handler_)
7113  {
7114  return _body.get().executeAsync(command_, handler_);
7115  }
7116 
7146  std::string executeAsyncNoResubscribe(Command& command_,
7147  MessageHandler handler_)
7148  {
7149  std::string id;
7150  try
7151  {
7152  if (command_.isSubscribe())
7153  {
7154  Message& message = command_.getMessage();
7155  Field subId = message.getSubscriptionId();
7156  bool useExistingHandler = !subId.empty() && !message.getOptions().empty() && message.getOptions().contains("replace", 7);
7157  if (useExistingHandler)
7158  {
7159  MessageHandler existingHandler;
7160  if (_body.get()._routes.getRoute(subId, existingHandler))
7161  {
7162  // we found an existing handler.
7163  _body.get().executeAsync(command_, existingHandler, false);
7164  return id; // empty string indicates existing
7165  }
7166  }
7167  }
7168  id = _body.get().executeAsync(command_, handler_, false);
7169  }
7170  catch (const DisconnectedException&)
7171  {
7172  removeMessageHandler(command_.getMessage().getCommandId());
7173  if (command_.isSubscribe())
7174  {
7175  removeMessageHandler(command_.getMessage().getSubscriptionId());
7176  }
7177  if (command_.isSow())
7178  {
7179  removeMessageHandler(command_.getMessage().getQueryID());
7180  }
7181  throw;
7182  }
7183  return id;
7184  }
7185 
7198  MessageStream execute(Command& command_);
7199 
7208  void ack(Field& topic_, Field& bookmark_, const char* options_ = NULL)
7209  {
7210  _body.get().ack(topic_, bookmark_, options_);
7211  }
7212 
7220  void ack(Message& message_, const char* options_ = NULL)
7221  {
7222  _body.get().ack(message_.getTopic(), message_.getBookmark(), options_);
7223  }
7232  void ack(const std::string& topic_, const std::string& bookmark_,
7233  const char* options_ = NULL)
7234  {
7235  _body.get().ack(Field(topic_.data(), topic_.length()), Field(bookmark_.data(), bookmark_.length()), options_);
7236  }
7237 
7243  void ackDeferredAutoAck(Field& topic_, Field& bookmark_, const char* options_ = NULL)
7244  {
7245  _body.get()._ack(topic_, bookmark_, options_);
7246  }
7256  void flushAcks(void)
7257  {
7258  _body.get().flushAcks();
7259  }
7260 
7265  bool getAutoAck(void) const
7266  {
7267  return _body.get().getAutoAck();
7268  }
7275  void setAutoAck(bool isAutoAckEnabled_)
7276  {
7277  _body.get().setAutoAck(isAutoAckEnabled_);
7278  }
7283  unsigned getAckBatchSize(void) const
7284  {
7285  return _body.get().getAckBatchSize();
7286  }
7293  void setAckBatchSize(const unsigned ackBatchSize_)
7294  {
7295  _body.get().setAckBatchSize(ackBatchSize_);
7296  }
7297 
7304  int getAckTimeout(void) const
7305  {
7306  return _body.get().getAckTimeout();
7307  }
7316  void setAckTimeout(const int ackTimeout_)
7317  {
7318  if (!ackTimeout_ && _body.get().getAckBatchSize() > 1)
7319  {
7320  throw UsageException("Ack timeout must be > 0 when ack batch size > 1");
7321  }
7322  _body.get().setAckTimeout(ackTimeout_);
7323  }
7324 
7325 
7334  void setRetryOnDisconnect(bool isRetryOnDisconnect_)
7335  {
7336  _body.get().setRetryOnDisconnect(isRetryOnDisconnect_);
7337  }
7338 
7343  bool getRetryOnDisconnect(void) const
7344  {
7345  return _body.get().getRetryOnDisconnect();
7346  }
7347 
7352  void setDefaultMaxDepth(unsigned maxDepth_)
7353  {
7354  _body.get().setDefaultMaxDepth(maxDepth_);
7355  }
7356 
7361  unsigned getDefaultMaxDepth(void) const
7362  {
7363  return _body.get().getDefaultMaxDepth();
7364  }
7365 
7373  void* userData_)
7374  {
7375  return _body.get().setTransportFilterFunction(filter_, userData_);
7376  }
7377 
7387  void* userData_)
7388  {
7389  return _body.get().setThreadCreatedCallback(callback_, userData_);
7390  }
7391 
7397  void deferredExecution(DeferredExecutionFunc func_, void* userData_)
7398  {
7399  _body.get().deferredExecution(func_, userData_);
7400  }
7404  };
7405 
7406  inline void
7407  ClientImpl::lastChance(AMPS::Message& message)
7408  {
7409  AMPS_CALL_EXCEPTION_WRAPPER(_globalCommandTypeHandlers[GlobalCommandTypeHandlers::LastChance].invoke(message));
7410  }
7411 
7412  inline unsigned
7413  ClientImpl::persistedAck(AMPS::Message& message)
7414  {
7415  unsigned deliveries = 0;
7416  try
7417  {
7418  /*
7419  * Best Practice: If you don't care about the dupe acks that
7420  * occur during failover or rapid disconnect/reconnect, then just
7421  * ignore them. We could discard each duplicate from the
7422  * persisted store, but the storage costs of doing 1 record
7423  * discards is heavy. In most scenarios we'll just quickly blow
7424  * through the duplicates and get back to processing the
7425  * non-dupes.
7426  */
7427  const char* data = NULL;
7428  size_t len = 0;
7429  const char* status = NULL;
7430  size_t statusLen = 0;
7431  amps_handle messageHandle = message.getMessage();
7432  const size_t NotEntitled = 12, Duplicate = 9, Failure = 7;
7433  amps_message_get_field_value(messageHandle, AMPS_Reason, &data, &len);
7434  amps_message_get_field_value(messageHandle, AMPS_Status, &status, &statusLen);
7435  if (len == NotEntitled || len == Duplicate ||
7436  (statusLen == Failure && status[0] == 'f'))
7437  {
7438  if (_failedWriteHandler)
7439  {
7440  if (_publishStore.isValid())
7441  {
7442  amps_uint64_t sequence =
7443  amps_message_get_field_uint64(messageHandle, AMPS_Sequence);
7444  FailedWriteStoreReplayer replayer(this, data, len);
7445  AMPS_CALL_EXCEPTION_WRAPPER(_publishStore.replaySingle(
7446  replayer, sequence));
7447  }
7448  else // Call the handler with what little we have
7449  {
7450  static Message emptyMessage;
7451  emptyMessage.setSequence(message.getSequence());
7452  AMPS_CALL_EXCEPTION_WRAPPER(
7453  _failedWriteHandler->failedWrite(emptyMessage,
7454  data, len));
7455  }
7456  ++deliveries;
7457  }
7458  }
7459  if (_publishStore.isValid())
7460  {
7461  // Ack for publisher will have sequence while
7462  // ack for bookmark subscribe won't
7463  amps_uint64_t seq = amps_message_get_field_uint64(messageHandle,
7464  AMPS_Sequence);
7465  if (seq > 0)
7466  {
7467  ++deliveries;
7468  AMPS_CALL_EXCEPTION_WRAPPER(_publishStore.discardUpTo(seq));
7469  }
7470  }
7471 
7472  if (!deliveries && _bookmarkStore.isValid())
7473  {
7474  amps_message_get_field_value(messageHandle, AMPS_SubscriptionId,
7475  &data, &len);
7476  if (len > 0)
7477  {
7478  Message::Field subId(data, len);
7479  const char* bookmarkData = NULL;
7480  size_t bookmarkLen = 0;
7481  amps_message_get_field_value(messageHandle,
7482  AMPS_Bookmark,
7483  &bookmarkData,
7484  &bookmarkLen);
7485  // Everything is there and not unsubscribed AC-912
7486  if (bookmarkLen > 0 && _routes.hasRoute(subId))
7487  {
7488  ++deliveries;
7489  _bookmarkStore.persisted(subId, Message::Field(bookmarkData, bookmarkLen));
7490  }
7491  }
7492  }
7493  }
7494  catch (std::exception& ex)
7495  {
7496  AMPS_UNHANDLED_EXCEPTION(ex);
7497  }
7498  return deliveries;
7499  }
7500 
7501  inline unsigned
7502  ClientImpl::processedAck(Message& message)
7503  {
7504  unsigned deliveries = 0;
7505  AckResponse ack;
7506  const char* data = NULL;
7507  size_t len = 0;
7508  amps_handle messageHandle = message.getMessage();
7509  amps_message_get_field_value(messageHandle, AMPS_CommandId, &data, &len);
7510  Lock<Mutex> l(_lock);
7511  if (data && len)
7512  {
7513  Lock<Mutex> guard(_ackMapLock);
7514  AckMap::iterator i = _ackMap.find(std::string(data, len));
7515  if (i != _ackMap.end())
7516  {
7517  ++deliveries;
7518  ack = i->second;
7519  _ackMap.erase(i);
7520  }
7521  }
7522  if (deliveries)
7523  {
7524  amps_message_get_field_value(messageHandle, AMPS_Status, &data, &len);
7525  ack.setStatus(data, len);
7526  amps_message_get_field_value(messageHandle, AMPS_Reason, &data, &len);
7527  ack.setReason(data, len);
7528  amps_message_get_field_value(messageHandle, AMPS_UserId, &data, &len);
7529  ack.setUsername(data, len);
7530  amps_message_get_field_value(messageHandle, AMPS_Password, &data, &len);
7531  ack.setPassword(data, len);
7532  amps_message_get_field_value(messageHandle, AMPS_Version, &data, &len);
7533  ack.setServerVersion(data, len);
7534  amps_message_get_field_value(messageHandle, AMPS_Options, &data, &len);
7535  ack.setOptions(data, len);
7536  // This sets bookmark, nameHashValue, and sequenceNo
7537  ack.setBookmark(message.getBookmark());
7538  ack.setResponded();
7539  _lock.signalAll();
7540  }
7541  return deliveries;
7542  }
7543 
7544  inline void
7545  ClientImpl::checkAndSendHeartbeat(bool force)
7546  {
7547  if (force || _heartbeatTimer.check())
7548  {
7549  _heartbeatTimer.start();
7550  try
7551  {
7552  sendWithoutRetry(_beatMessage);
7553  }
7554  catch (const AMPSException&)
7555  {
7556  ;
7557  }
7558  }
7559  }
7560 
7561  inline ConnectionInfo ClientImpl::getConnectionInfo() const
7562  {
7563  ConnectionInfo info;
7564  std::ostringstream writer;
7565 
7566  info["client.uri"] = _lastUri;
7567  info["client.name"] = _name;
7568  info["client.username"] = _username;
7569  if (_publishStore.isValid())
7570  {
7571  writer << _publishStore.unpersistedCount();
7572  info["publishStore.unpersistedCount"] = writer.str();
7573  writer.clear();
7574  writer.str("");
7575  }
7576 
7577  return info;
7578  }
7579 
7580  inline amps_result
7581  ClientImpl::ClientImplMessageHandler(amps_handle messageHandle_, void* userData_)
7582  {
7583  const unsigned SOWMask = Message::Command::SOW | Message::Command::GroupBegin | Message::Command::GroupEnd;
7584  const unsigned PublishMask = Message::Command::OOF | Message::Command::Publish | Message::Command::DeltaPublish;
7585  ClientImpl* me = (ClientImpl*) userData_;
7586  AMPS_CALL_EXCEPTION_WRAPPER_2(me, me->processDeferredExecutions());
7587  if (!messageHandle_)
7588  {
7589  if (me->_queueAckTimeout)
7590  {
7591  me->checkQueueAcks();
7592  }
7593  return AMPS_E_OK;
7594  }
7595 
7596  me->_readMessage.replace(messageHandle_);
7597  Message& message = me->_readMessage;
7598  Message::Command::Type commandType = message.getCommandEnum();
7599  if (commandType & SOWMask)
7600  {
7601 #if 0 // Not currently implemented, to avoid an extra branch in delivery
7602  // A small cheat here to get the right handler, using knowledge of the
7603  // Command values of SOW (8), GroupBegin (8192), and GroupEnd (16384)
7604  // and their GlobalCommandTypeHandlers values 1, 2, 3.
7605  AMPS_CALL_EXCEPTION_WRAPPER_2(me,
7606  me->_globalCommandTypeHandlers[1 + (commandType / 8192)].invoke(message));
7607 #endif
7608  AMPS_CALL_EXCEPTION_WRAPPER_2(me, me->_routes.deliverData(message,
7609  message.getQueryID()));
7610  }
7611  else if (commandType & PublishMask)
7612  {
7613 #if 0 // Not currently implemented, to avoid an extra branch in delivery
7614  AMPS_CALL_EXCEPTION_WRAPPER_2(me,
7615  me->_globalCommandTypeHandlers[(commandType == Message::Command::Publish ?
7616  GlobalCommandTypeHandlers::Publish :
7617  GlobalCommandTypeHandlers::OOF)].invoke(message));
7618 #endif
7619  const char* subIds = NULL;
7620  size_t subIdsLen = 0;
7621  // Publish command, send to subscriptions
7622  amps_message_get_field_value(messageHandle_, AMPS_SubscriptionIds,
7623  &subIds, &subIdsLen);
7624  size_t subIdCount = me->_routes.parseRoutes(AMPS::Field(subIds, subIdsLen), me->_routeCache);
7625  for (size_t i = 0; i < subIdCount; ++i)
7626  {
7627  MessageRouter::RouteCache::value_type& lookupResult = me->_routeCache[i];
7628  MessageHandler& handler = lookupResult.handler;
7629  if (handler.isValid())
7630  {
7631  amps_message_set_field_value(messageHandle_,
7632  AMPS_SubscriptionId,
7633  subIds + lookupResult.idOffset,
7634  lookupResult.idLength);
7635  Message::Field bookmark = message.getBookmark();
7636  bool isMessageQueue = message.getLeasePeriod().len() != 0;
7637  bool isAutoAck = me->_isAutoAckEnabled;
7638 
7639  if (!isMessageQueue && !bookmark.empty() &&
7640  me->_bookmarkStore.isValid())
7641  {
7642  if (me->_bookmarkStore.isDiscarded(me->_readMessage))
7643  {
7644  //Call duplicate message handler in handlers map
7645  if (me->_globalCommandTypeHandlers[GlobalCommandTypeHandlers::DuplicateMessage].isValid())
7646  {
7647  AMPS_CALL_EXCEPTION_WRAPPER_2(me, me->_globalCommandTypeHandlers[GlobalCommandTypeHandlers::DuplicateMessage].invoke(message));
7648  }
7649  }
7650  else
7651  {
7652  me->_bookmarkStore.log(me->_readMessage);
7653  AMPS_CALL_EXCEPTION_WRAPPER_2(me,
7654  handler.invoke(message));
7655  }
7656  }
7657  else
7658  {
7659  if (isMessageQueue && isAutoAck)
7660  {
7661  try
7662  {
7663  AMPS_CALL_EXCEPTION_WRAPPER_STREAM_FULL_2(me, handler.invoke(message));
7664  if (!message.getIgnoreAutoAck())
7665  {
7666  AMPS_CALL_EXCEPTION_WRAPPER_2(me,
7667  me->_ack(message.getTopic(), message.getBookmark()));
7668  }
7669  }
7670  catch (std::exception& ex)
7671  {
7672  if (!message.getIgnoreAutoAck())
7673  {
7674  AMPS_CALL_EXCEPTION_WRAPPER_2(me,
7675  me->_ack(message.getTopic(), message.getBookmark(), "cancel"));
7676  }
7677  AMPS_UNHANDLED_EXCEPTION_2(me, ex);
7678  }
7679  }
7680  else
7681  {
7682  AMPS_CALL_EXCEPTION_WRAPPER_2(me,
7683  handler.invoke(message));
7684  }
7685  }
7686  }
7687  else
7688  {
7689  me->lastChance(message);
7690  }
7691  } // for (subidsEnd)
7692  }
7693  else if (commandType == Message::Command::Ack)
7694  {
7695  AMPS_CALL_EXCEPTION_WRAPPER_2(me,
7696  me->_globalCommandTypeHandlers[GlobalCommandTypeHandlers::Ack].invoke(message));
7697  unsigned ackType = message.getAckTypeEnum();
7698  unsigned deliveries = 0U;
7699  switch (ackType)
7700  {
7701  case Message::AckType::Persisted:
7702  deliveries += me->persistedAck(message);
7703  break;
7704  case Message::AckType::Processed: // processed
7705  deliveries += me->processedAck(message);
7706  break;
7707  }
7708  AMPS_CALL_EXCEPTION_WRAPPER_2(me, deliveries += me->_routes.deliverAck(message, ackType));
7709  if (deliveries == 0)
7710  {
7711  me->lastChance(message);
7712  }
7713  }
7714  else if (commandType == Message::Command::Heartbeat)
7715  {
7716  AMPS_CALL_EXCEPTION_WRAPPER_2(me,
7717  me->_globalCommandTypeHandlers[GlobalCommandTypeHandlers::Heartbeat].invoke(message));
7718  if (me->_heartbeatTimer.getTimeout() != 0.0) // -V550
7719  {
7720  me->checkAndSendHeartbeat(true);
7721  }
7722  else
7723  {
7724  me->lastChance(message);
7725  }
7726  return AMPS_E_OK;
7727  }
7728  else if (!message.getCommandId().empty())
7729  {
7730  unsigned deliveries = 0U;
7731  try
7732  {
7733  while (me->_connected) // Keep sending heartbeats when stream is full
7734  {
7735  try
7736  {
7737  deliveries = me->_routes.deliverData(message, message.getCommandId());
7738  break;
7739  }
7740 #ifdef _WIN32
7741  catch (MessageStreamFullException&)
7742 #else
7743  catch (MessageStreamFullException& ex_)
7744 #endif
7745  {
7746  me->checkAndSendHeartbeat(false);
7747  }
7748  }
7749  }
7750  catch (std::exception& ex_)
7751  {
7752  try
7753  {
7754  me->_exceptionListener->exceptionThrown(ex_);
7755  }
7756  catch (...)
7757  {
7758  ;
7759  }
7760  }
7761  if (deliveries == 0)
7762  {
7763  me->lastChance(message);
7764  }
7765  }
7766  me->checkAndSendHeartbeat();
7767  return AMPS_E_OK;
7768  }
7769 
7770  inline void
7771  ClientImpl::ClientImplPreDisconnectHandler(amps_handle /*client*/, unsigned failedConnectionVersion, void* userData)
7772  {
7773  ClientImpl* me = (ClientImpl*) userData;
7774  //Client wrapper(me);
7775  // Go ahead and signal any waiters if they are around...
7776  me->clearAcks(failedConnectionVersion);
7777  }
7778 
7779  inline amps_result
7780  ClientImpl::ClientImplDisconnectHandler(amps_handle /*client*/, void* userData)
7781  {
7782  ClientImpl* me = (ClientImpl*) userData;
7783  Lock<Mutex> l(me->_lock);
7784  Client wrapper(me, false);
7785  if (me->_connected)
7786  {
7787  me->broadcastConnectionStateChanged(ConnectionStateListener::Disconnected);
7788  }
7789  while (true)
7790  {
7791  AtomicFlagFlip subFlip(&me->_badTimeToHASubscribe);
7792  try
7793  {
7794  AtomicFlagFlip pubFlip(&me->_badTimeToHAPublish);
7795  me->_connected = false;
7796  {
7797  // Have to release the lock here or receive thread can't
7798  // invoke the message handler.
7799  Unlock<Mutex> unlock(me->_lock);
7800  me->_disconnectHandler.invoke(wrapper);
7801  }
7802  }
7803  catch (const std::exception& ex)
7804  {
7805  AMPS_UNHANDLED_EXCEPTION_2(me, ex);
7806  }
7807  me->_lock.signalAll();
7808 
7809  if (!me->_connected)
7810  {
7811  me->broadcastConnectionStateChanged(ConnectionStateListener::Shutdown);
7812  AMPS_UNHANDLED_EXCEPTION_2(me, DisconnectedException("Reconnect failed."));
7813  return AMPS_E_DISCONNECTED;
7814  }
7815  try
7816  {
7817  // Resubscribe
7818  if (me->_subscriptionManager)
7819  {
7820  {
7821  // Have to release the lock here or receive thread can't
7822  // invoke the message handler.
7823  Unlock<Mutex> unlock(me->_lock);
7824  me->_subscriptionManager->resubscribe(wrapper);
7825  }
7826  me->broadcastConnectionStateChanged(ConnectionStateListener::Resubscribed);
7827  }
7828  return AMPS_E_OK;
7829  }
7830  catch (const AMPSException& subEx)
7831  {
7832  AMPS_UNHANDLED_EXCEPTION_2(me, subEx);
7833  }
7834  catch (const std::exception& subEx)
7835  {
7836  AMPS_UNHANDLED_EXCEPTION_2(me, subEx);
7837  return AMPS_E_RETRY;
7838  }
7839  catch (...)
7840  {
7841  return AMPS_E_RETRY;
7842  }
7843  }
7844  return AMPS_E_RETRY;
7845  }
7846 
7847  class FIX
7848  {
7849  const char* _data;
7850  size_t _len;
7851  char _fieldSep;
7852  public:
7853  class iterator
7854  {
7855  const char* _data;
7856  size_t _len;
7857  size_t _pos;
7858  char _fieldSep;
7859  iterator(const char* data_, size_t len_, size_t pos_, char fieldSep_)
7860  : _data(data_), _len(len_), _pos(pos_), _fieldSep(fieldSep_)
7861  {
7862  while (_pos != _len && _data[_pos] == _fieldSep)
7863  {
7864  ++_pos;
7865  }
7866  }
7867  public:
7868  typedef void* difference_type;
7869  typedef std::forward_iterator_tag iterator_category;
7870  typedef std::pair<Message::Field, Message::Field> value_type;
7871  typedef value_type* pointer;
7872  typedef value_type& reference;
7873  bool operator==(const iterator& rhs) const
7874  {
7875  return _pos == rhs._pos;
7876  }
7877  bool operator!=(const iterator& rhs) const
7878  {
7879  return _pos != rhs._pos;
7880  }
7881  iterator& operator++()
7882  {
7883  // Skip through the data
7884  while (_pos != _len && _data[_pos] != _fieldSep)
7885  {
7886  ++_pos;
7887  }
7888  // Skip through any field separators
7889  while (_pos != _len && _data[_pos] == _fieldSep)
7890  {
7891  ++_pos;
7892  }
7893  return *this;
7894  }
7895 
7896  value_type operator*() const
7897  {
7898  value_type result;
7899  size_t i = _pos, keyLength = 0, valueStart = 0, valueLength = 0;
7900  for (; i < _len && _data[i] != '='; ++i)
7901  {
7902  ++keyLength;
7903  }
7904 
7905  result.first.assign(_data + _pos, keyLength);
7906 
7907  if (i < _len && _data[i] == '=')
7908  {
7909  ++i;
7910  valueStart = i;
7911  for (; i < _len && _data[i] != _fieldSep; ++i)
7912  {
7913  valueLength++;
7914  }
7915  }
7916  result.second.assign(_data + valueStart, valueLength);
7917  return result;
7918  }
7919 
7920  friend class FIX;
7921  };
7922  class reverse_iterator
7923  {
7924  const char* _data;
7925  size_t _len;
7926  const char* _pos;
7927  char _fieldSep;
7928  public:
7929  typedef std::pair<Message::Field, Message::Field> value_type;
7930  reverse_iterator(const char* data, size_t len, const char* pos, char fieldsep)
7931  : _data(data), _len(len), _pos(pos), _fieldSep(fieldsep)
7932  {
7933  if (_pos)
7934  {
7935  // skip past meaningless trailing fieldseps
7936  while (_pos >= _data && *_pos == _fieldSep)
7937  {
7938  --_pos;
7939  }
7940  while (_pos > _data && *_pos != _fieldSep)
7941  {
7942  --_pos;
7943  }
7944  // if we stopped before the 0th character, it's because
7945  // it's a field sep. advance one to point to the first character
7946  // of a key.
7947  if (_pos > _data || (_pos == _data && *_pos == _fieldSep))
7948  {
7949  ++_pos;
7950  }
7951  if (_pos < _data)
7952  {
7953  _pos = 0;
7954  }
7955  }
7956  }
7957  bool operator==(const reverse_iterator& rhs) const
7958  {
7959  return _pos == rhs._pos;
7960  }
7961  bool operator!=(const reverse_iterator& rhs) const
7962  {
7963  return _pos != rhs._pos;
7964  }
7965  reverse_iterator& operator++()
7966  {
7967  if (_pos == _data)
7968  {
7969  _pos = 0;
7970  }
7971  else
7972  {
7973  // back up 1 to a field separator
7974  --_pos;
7975  // keep backing up through field separators
7976  while (_pos >= _data && *_pos == _fieldSep)
7977  {
7978  --_pos;
7979  }
7980  // now back up to the beginning of this field
7981  while (_pos > _data && *_pos != _fieldSep)
7982  {
7983  --_pos;
7984  }
7985  if (_pos > _data || (_pos == _data && *_pos == _fieldSep))
7986  {
7987  ++_pos;
7988  }
7989  if (_pos < _data)
7990  {
7991  _pos = 0;
7992  }
7993  }
7994  return *this;
7995  }
7996  value_type operator*() const
7997  {
7998  value_type result;
7999  size_t keyLength = 0, valueStart = 0, valueLength = 0;
8000  size_t i = (size_t)(_pos - _data);
8001  for (; i < _len && _data[i] != '='; ++i)
8002  {
8003  ++keyLength;
8004  }
8005  result.first.assign(_pos, keyLength);
8006  if (i < _len && _data[i] == '=')
8007  {
8008  ++i;
8009  valueStart = i;
8010  for (; i < _len && _data[i] != _fieldSep; ++i)
8011  {
8012  valueLength++;
8013  }
8014  }
8015  result.second.assign(_data + valueStart, valueLength);
8016  return result;
8017  }
8018  };
8019  FIX(const Message::Field& data, char fieldSeparator = 1)
8020  : _data(data.data()), _len(data.len()),
8021  _fieldSep(fieldSeparator)
8022  {
8023  }
8024 
8025  FIX(const char* data, size_t len, char fieldSeparator = 1)
8026  : _data(data), _len(len), _fieldSep(fieldSeparator)
8027  {
8028  }
8029 
8030  iterator begin() const
8031  {
8032  return iterator(_data, _len, 0, _fieldSep);
8033  }
8034  iterator end() const
8035  {
8036  return iterator(_data, _len, _len, _fieldSep);
8037  }
8038 
8039 
8040  reverse_iterator rbegin() const
8041  {
8042  return reverse_iterator(_data, _len, _data + (_len - 1), _fieldSep);
8043  }
8044 
8045  reverse_iterator rend() const
8046  {
8047  return reverse_iterator(_data, _len, 0, _fieldSep);
8048  }
8049  };
8050 
8051 
8064 
8065  template <class T>
8067  {
8068  std::stringstream _data;
8069  char _fs;
8070  public:
8076  _FIXBuilder(char fieldSep_ = (char)1) : _fs(fieldSep_) {;}
8077 
8085  void append(const T& tag, const char* value, size_t offset, size_t length)
8086  {
8087  _data << tag << '=';
8088  _data.write(value + offset, (std::streamsize)length);
8089  _data << _fs;
8090  }
8096  void append(const T& tag, const std::string& value)
8097  {
8098  _data << tag << '=' << value << _fs;
8099  }
8100 
8103  std::string getString() const
8104  {
8105  return _data.str();
8106  }
8107  operator std::string() const
8108  {
8109  return _data.str();
8110  }
8111 
8113  void reset()
8114  {
8115  _data.str(std::string());
8116  }
8117  };
8118 
8122 
8124 
8128 
8130 
8131 
8139 
8141  {
8142  char _fs;
8143  public:
8148  FIXShredder(char fieldSep_ = (char)1) : _fs(fieldSep_) {;}
8149 
8152  typedef std::map<Message::Field, Message::Field> map_type;
8153 
8159  map_type toMap(const Message::Field& data)
8160  {
8161  FIX fix(data, _fs);
8162  map_type retval;
8163  for (FIX::iterator a = fix.begin(); a != fix.end(); ++a)
8164  {
8165  retval.insert(*a);
8166  }
8167 
8168  return retval;
8169  }
8170  };
8171 
8172 #define AMPS_MESSAGE_STREAM_CACHE_MAX 128
8173  class MessageStreamImpl : public AMPS::RefBody, AMPS::ConnectionStateListener
8174  {
8175  Mutex _lock;
8176  std::deque<Message> _q;
8177  std::deque<Message> _cache;
8178  std::string _commandId;
8179  std::string _subId;
8180  std::string _queryId;
8181  Client _client;
8182  unsigned _timeout;
8183  unsigned _maxDepth;
8184  unsigned _requestedAcks;
8185  size_t _cacheMax;
8186  Message::Field _previousTopic;
8187  Message::Field _previousBookmark;
8188  typedef enum : unsigned int { Unset = 0x0, Running = 0x10, Subscribe = 0x11, SOWOnly = 0x12, AcksOnly = 0x13, Conflate = 0x14, Closed = 0x1, Disconnected = 0x2 } State;
8189 #if __cplusplus >= 201100L || _MSC_VER >= 1900
8190  std::atomic<State> _state;
8191 #else
8192  volatile State _state;
8193 #endif
8194  typedef std::map<std::string, Message*> SOWKeyMap;
8195  SOWKeyMap _sowKeyMap;
8196  public:
8197  MessageStreamImpl(const Client& client_)
8198  : _client(client_),
8199  _timeout(0),
8200  _maxDepth((unsigned)~0),
8201  _requestedAcks(0),
8202  _cacheMax(AMPS_MESSAGE_STREAM_CACHE_MAX),
8203  _state(Unset)
8204  {
8205  if (_client.isValid())
8206  {
8207  _client.addConnectionStateListener(this);
8208  }
8209  }
8210 
8211  MessageStreamImpl(ClientImpl* client_)
8212  : _client(client_),
8213  _timeout(0),
8214  _maxDepth((unsigned)~0),
8215  _requestedAcks(0),
8216  _state(Unset)
8217  {
8218  if (_client.isValid())
8219  {
8220  _client.addConnectionStateListener(this);
8221  }
8222  }
8223 
8224  ~MessageStreamImpl()
8225  {
8226  }
8227 
8228  virtual void destroy()
8229  {
8230  try
8231  {
8232  close();
8233  }
8234  catch (std::exception& e)
8235  {
8236  try
8237  {
8238  if (_client.isValid())
8239  {
8240  _client.getExceptionListener().exceptionThrown(e);
8241  }
8242  }
8243  catch (...) {/*Ignore exception listener exceptions*/} // -V565
8244  }
8245  if (_client.isValid())
8246  {
8247  _client.removeConnectionStateListener(this);
8248  Client c = _client;
8249  _client = Client((ClientImpl*)NULL);
8250  c.deferredExecution(MessageStreamImpl::destroyer, this);
8251  }
8252  else
8253  {
8254  delete this;
8255  }
8256  }
8257 
8258  static void destroyer(void* vpMessageStreamImpl_)
8259  {
8260  delete ((MessageStreamImpl*)vpMessageStreamImpl_);
8261  }
8262 
8263  void setSubscription(const std::string& subId_,
8264  const std::string& commandId_ = "",
8265  const std::string& queryId_ = "")
8266  {
8267  Lock<Mutex> lock(_lock);
8268  _subId = subId_;
8269  if (!commandId_.empty() && commandId_ != subId_)
8270  {
8271  _commandId = commandId_;
8272  }
8273  if (!queryId_.empty() && queryId_ != subId_ && queryId_ != commandId_)
8274  {
8275  _queryId = queryId_;
8276  }
8277  // It's possible to disconnect between creation/registration and here.
8278  if (Disconnected == _state)
8279  {
8280  return;
8281  }
8282  assert(Unset == _state);
8283  _state = Subscribe;
8284  }
8285 
8286  void setSOWOnly(const std::string& commandId_,
8287  const std::string& queryId_ = "")
8288  {
8289  Lock<Mutex> lock(_lock);
8290  _commandId = commandId_;
8291  if (!queryId_.empty() && queryId_ != commandId_)
8292  {
8293  _queryId = queryId_;
8294  }
8295  // It's possible to disconnect between creation/registration and here.
8296  if (Disconnected == _state)
8297  {
8298  return;
8299  }
8300  assert(Unset == _state);
8301  _state = SOWOnly;
8302  }
8303 
8304  void setStatsOnly(const std::string& commandId_,
8305  const std::string& queryId_ = "")
8306  {
8307  Lock<Mutex> lock(_lock);
8308  _commandId = commandId_;
8309  if (!queryId_.empty() && queryId_ != commandId_)
8310  {
8311  _queryId = queryId_;
8312  }
8313  // It's possible to disconnect between creation/registration and here.
8314  if (Disconnected == _state)
8315  {
8316  return;
8317  }
8318  assert(Unset == _state);
8319  _state = AcksOnly;
8320  _requestedAcks = Message::AckType::Stats;
8321  }
8322 
8323  void setAcksOnly(const std::string& commandId_, unsigned acks_)
8324  {
8325  Lock<Mutex> lock(_lock);
8326  _commandId = commandId_;
8327  // It's possible to disconnect between creation/registration and here.
8328  if (Disconnected == _state)
8329  {
8330  return;
8331  }
8332  assert(Unset == _state);
8333  _state = AcksOnly;
8334  _requestedAcks = acks_;
8335  }
8336 
8337  void connectionStateChanged(ConnectionStateListener::State state_)
8338  {
8339  Lock<Mutex> lock(_lock);
8340  if (state_ == AMPS::ConnectionStateListener::Disconnected)
8341  {
8342  _state = Disconnected;
8343  close();
8344  }
8345  _lock.signalAll();
8346  }
8347 
8348  void timeout(unsigned timeout_)
8349  {
8350  _timeout = timeout_;
8351  }
8352  void conflate(void)
8353  {
8354  if (_state == Subscribe)
8355  {
8356  _state = Conflate;
8357  }
8358  }
8359  void maxDepth(unsigned maxDepth_)
8360  {
8361  if (maxDepth_)
8362  {
8363  _maxDepth = maxDepth_;
8364  }
8365  else
8366  {
8367  _maxDepth = (unsigned)~0;
8368  }
8369  }
8370  unsigned getMaxDepth(void) const
8371  {
8372  return _maxDepth;
8373  }
8374  unsigned getDepth(void) const
8375  {
8376  return (unsigned)(_q.size());
8377  }
8378 
8379  bool next(Message& current_)
8380  {
8381  Lock<Mutex> lock(_lock);
8382  if (!_previousTopic.empty() && !_previousBookmark.empty())
8383  {
8384  try
8385  {
8386  if (_client.isValid())
8387  {
8388  _client.ackDeferredAutoAck(_previousTopic, _previousBookmark);
8389  }
8390  }
8391 #ifdef _WIN32
8392  catch (AMPSException&)
8393 #else
8394  catch (AMPSException& e)
8395 #endif
8396  {
8397  current_.invalidate();
8398  _previousTopic.clear();
8399  _previousBookmark.clear();
8400  return false;
8401  }
8402  _previousTopic.clear();
8403  _previousBookmark.clear();
8404  }
8405  double minWaitTime = (double)((_timeout && _timeout > 1000)
8406  ? _timeout : 1000);
8407  Timer timer(minWaitTime);
8408  timer.start();
8409  while (_q.empty() && _state & Running)
8410  {
8411  // Using timeout so python can interrupt
8412  _lock.wait((long)minWaitTime);
8413  {
8414  Unlock<Mutex> unlck(_lock);
8415  amps_invoke_waiting_function();
8416  }
8417  if (_timeout)
8418  {
8419  // In case we woke up early, see how much longer to wait
8420  if (timer.checkAndGetRemaining(&minWaitTime))
8421  {
8422  break;
8423  }
8424  }
8425  }
8426  if (current_.isValid() && _cache.size() < _cacheMax)
8427  {
8428  current_.reset();
8429  _cache.push_back(current_);
8430  }
8431  if (!_q.empty())
8432  {
8433  current_ = _q.front();
8434  if (_q.size() == _maxDepth)
8435  {
8436  _lock.signalAll();
8437  }
8438  _q.pop_front();
8439  if (_state == Conflate)
8440  {
8441  std::string sowKey = current_.getSowKey();
8442  if (sowKey.length())
8443  {
8444  _sowKeyMap.erase(sowKey);
8445  }
8446  }
8447  else if (_state == AcksOnly)
8448  {
8449  _requestedAcks &= ~(current_.getAckTypeEnum());
8450  }
8451  if ((_state == AcksOnly && _requestedAcks == 0) ||
8452  (_state == SOWOnly && current_.getCommand() == "group_end"))
8453  {
8454  _state = Closed;
8455  }
8456  else if (current_.getCommandEnum() == Message::Command::Publish &&
8457  _client.isValid() && _client.getAutoAck() &&
8458  !current_.getLeasePeriod().empty() &&
8459  !current_.getBookmark().empty())
8460  {
8461  _previousTopic = current_.getTopic().deepCopy();
8462  _previousBookmark = current_.getBookmark().deepCopy();
8463  }
8464  return true;
8465  }
8466  if (_state == Disconnected)
8467  {
8468  throw DisconnectedException("Connection closed.");
8469  }
8470  current_.invalidate();
8471  if (_state == Closed)
8472  {
8473  return false;
8474  }
8475  return _timeout != 0;
8476  }
8477  void close(void)
8478  {
8479  if (_client.isValid())
8480  {
8481  if (_state == SOWOnly || _state == Subscribe) //not delete
8482  {
8483  if (!_commandId.empty())
8484  {
8485  _client.unsubscribe(_commandId);
8486  }
8487  if (!_subId.empty())
8488  {
8489  _client.unsubscribe(_subId);
8490  }
8491  if (!_queryId.empty())
8492  {
8493  _client.unsubscribe(_queryId);
8494  }
8495  }
8496  else
8497  {
8498  if (!_commandId.empty())
8499  {
8500  _client.removeMessageHandler(_commandId);
8501  }
8502  if (!_subId.empty())
8503  {
8504  _client.removeMessageHandler(_subId);
8505  }
8506  if (!_queryId.empty())
8507  {
8508  _client.removeMessageHandler(_queryId);
8509  }
8510  }
8511  }
8512  if (_state == SOWOnly || _state == Subscribe || _state == Unset)
8513  {
8514  _state = Closed;
8515  }
8516  }
8517  static void _messageHandler(const Message& message_, MessageStreamImpl* this_)
8518  {
8519  Lock<Mutex> lock(this_->_lock);
8520  if (this_->_state != Conflate)
8521  {
8522  AMPS_TESTING_SLOW_MESSAGE_STREAM
8523  if (this_->_q.size() >= this_->_maxDepth)
8524  {
8525  // We throw here so that heartbeats can be sent. The exception
8526  // will be handled internally only, and the same Message will
8527  // come back to try again. Make sure to signal.
8528  this_->_lock.signalAll();
8529  throw MessageStreamFullException("Stream is currently full.");
8530  }
8531  if (!this_->_cache.empty())
8532  {
8533  this_->_cache.front().deepCopy(message_);
8534  this_->_q.push_back(this_->_cache.front());
8535  this_->_cache.pop_front();
8536  }
8537  else
8538  {
8539 #ifdef AMPS_USE_EMPLACE
8540  this_->_q.emplace_back(message_.deepCopy());
8541 #else
8542  this_->_q.push_back(message_.deepCopy());
8543 #endif
8544  }
8545  if (message_.getCommandEnum() == Message::Command::Publish &&
8546  this_->_client.isValid() && this_->_client.getAutoAck() &&
8547  !message_.getLeasePeriod().empty() &&
8548  !message_.getBookmark().empty())
8549  {
8550  message_.setIgnoreAutoAck();
8551  }
8552  }
8553  else
8554  {
8555  std::string sowKey = message_.getSowKey();
8556  if (sowKey.length())
8557  {
8558  SOWKeyMap::iterator it = this_->_sowKeyMap.find(sowKey);
8559  if (it != this_->_sowKeyMap.end())
8560  {
8561  it->second->deepCopy(message_);
8562  }
8563  else
8564  {
8565  if (this_->_q.size() >= this_->_maxDepth)
8566  {
8567  // We throw here so that heartbeats can be sent. The
8568  // exception will be handled internally only, and the
8569  // same Message will come back to try again. Make sure
8570  // to signal.
8571  this_->_lock.signalAll();
8572  throw MessageStreamFullException("Stream is currently full.");
8573  }
8574  if (!this_->_cache.empty())
8575  {
8576  this_->_cache.front().deepCopy(message_);
8577  this_->_q.push_back(this_->_cache.front());
8578  this_->_cache.pop_front();
8579  }
8580  else
8581  {
8582 #ifdef AMPS_USE_EMPLACE
8583  this_->_q.emplace_back(message_.deepCopy());
8584 #else
8585  this_->_q.push_back(message_.deepCopy());
8586 #endif
8587  }
8588  this_->_sowKeyMap[sowKey] = &(this_->_q.back());
8589  }
8590  }
8591  else
8592  {
8593  if (this_->_q.size() >= this_->_maxDepth)
8594  {
8595  // We throw here so that heartbeats can be sent. The exception
8596  // will be handled internally only, and the same Message will
8597  // come back to try again. Make sure to signal.
8598  this_->_lock.signalAll();
8599  throw MessageStreamFullException("Stream is currently full.");
8600  }
8601  if (!this_->_cache.empty())
8602  {
8603  this_->_cache.front().deepCopy(message_);
8604  this_->_q.push_back(this_->_cache.front());
8605  this_->_cache.pop_front();
8606  }
8607  else
8608  {
8609 #ifdef AMPS_USE_EMPLACE
8610  this_->_q.emplace_back(message_.deepCopy());
8611 #else
8612  this_->_q.push_back(message_.deepCopy());
8613 #endif
8614  }
8615  if (message_.getCommandEnum() == Message::Command::Publish &&
8616  this_->_client.isValid() && this_->_client.getAutoAck() &&
8617  !message_.getLeasePeriod().empty() &&
8618  !message_.getBookmark().empty())
8619  {
8620  message_.setIgnoreAutoAck();
8621  }
8622  }
8623  }
8624  this_->_lock.signalAll();
8625  }
8626  };
8627  inline MessageStream::MessageStream(void)
8628  {
8629  }
8630  inline MessageStream::MessageStream(const Client& client_)
8631  : _body(new MessageStreamImpl(client_))
8632  {
8633  }
8634  inline void MessageStream::iterator::advance(void)
8635  {
8636  _pStream = _pStream->_body->next(_current) ? _pStream : NULL;
8637  }
8638  inline MessageStream::operator MessageHandler(void)
8639  {
8640  return MessageHandler((void(*)(const Message&, void*))MessageStreamImpl::_messageHandler, &_body.get());
8641  }
8642  inline MessageStream MessageStream::fromExistingHandler(const MessageHandler& handler_)
8643  {
8644  MessageStream result;
8645  if (handler_._func == (MessageHandler::FunctionType)MessageStreamImpl::_messageHandler)
8646  {
8647  result._body = (MessageStreamImpl*)(handler_._userData);
8648  }
8649  return result;
8650  }
8651 
8652  inline void MessageStream::setSOWOnly(const std::string& commandId_,
8653  const std::string& queryId_)
8654  {
8655  _body->setSOWOnly(commandId_, queryId_);
8656  }
8657  inline void MessageStream::setSubscription(const std::string& subId_,
8658  const std::string& commandId_,
8659  const std::string& queryId_)
8660  {
8661  _body->setSubscription(subId_, commandId_, queryId_);
8662  }
8663  inline void MessageStream::setStatsOnly(const std::string& commandId_,
8664  const std::string& queryId_)
8665  {
8666  _body->setStatsOnly(commandId_, queryId_);
8667  }
8668  inline void MessageStream::setAcksOnly(const std::string& commandId_,
8669  unsigned acks_)
8670  {
8671  _body->setAcksOnly(commandId_, acks_);
8672  }
8673  inline MessageStream MessageStream::timeout(unsigned timeout_)
8674  {
8675  _body->timeout(timeout_);
8676  return *this;
8677  }
8679  {
8680  _body->conflate();
8681  return *this;
8682  }
8683  inline MessageStream MessageStream::maxDepth(unsigned maxDepth_)
8684  {
8685  _body->maxDepth(maxDepth_);
8686  return *this;
8687  }
8688  inline unsigned MessageStream::getMaxDepth(void) const
8689  {
8690  return _body->getMaxDepth();
8691  }
8692  inline unsigned MessageStream::getDepth(void) const
8693  {
8694  return _body->getDepth();
8695  }
8696 
8697  inline MessageStream ClientImpl::getEmptyMessageStream(void)
8698  {
8699  return *(_pEmptyMessageStream.get());
8700  }
8701 
8703  {
8704  // If the command is sow and has a sub_id, OR
8705  // if the command has a replace option, return the existing
8706  // messagestream, don't create a new one.
8707  ClientImpl& body = _body.get();
8708  Message& message = command_.getMessage();
8709  Field subId = message.getSubscriptionId();
8710  unsigned ackTypes = message.getAckTypeEnum();
8711  bool useExistingHandler = !subId.empty() && ((!message.getOptions().empty() && message.getOptions().contains("replace", 7)) || message.getCommandEnum() == Message::Command::SOW);
8712  if (useExistingHandler)
8713  {
8714  // Try to find the existing message handler.
8715  if (!subId.empty())
8716  {
8717  MessageHandler existingHandler;
8718  if (body._routes.getRoute(subId, existingHandler))
8719  {
8720  // we found an existing handler. It might not be a message stream, but that's okay.
8721  body.executeAsync(command_, existingHandler, false);
8722  return MessageStream::fromExistingHandler(existingHandler);
8723  }
8724  }
8725  // fall through; we'll a new handler altogether.
8726  }
8727  // Make sure something will be returned to the stream or use the empty one
8728  // Check that: it's a command that doesn't normally return data, and there
8729  // are no acks requested for the cmd id
8730  Message::Command::Type command = message.getCommandEnum();
8731  if ((command & Message::Command::NoDataCommands)
8732  && (ackTypes == Message::AckType::Persisted
8733  || ackTypes == Message::AckType::None))
8734  {
8735  executeAsync(command_, MessageHandler());
8736  if (!body._pEmptyMessageStream)
8737  {
8738  body._pEmptyMessageStream.reset(new MessageStream((ClientImpl*)0));
8739  body._pEmptyMessageStream.get()->_body->close();
8740  }
8741  return body.getEmptyMessageStream();
8742  }
8743  MessageStream stream(*this);
8744  if (body.getDefaultMaxDepth())
8745  {
8746  stream.maxDepth(body.getDefaultMaxDepth());
8747  }
8748  MessageHandler handler = stream.operator MessageHandler();
8749  std::string commandID = body.executeAsync(command_, handler, false);
8750  if (command_.hasStatsAck())
8751  {
8752  stream.setStatsOnly(commandID, command_.getMessage().getQueryId());
8753  }
8754  else if (command_.isSow())
8755  {
8756  stream.setSOWOnly(commandID, command_.getMessage().getQueryId());
8757  }
8758  else if (command_.isSubscribe())
8759  {
8760  stream.setSubscription(commandID,
8761  command_.getMessage().getCommandId(),
8762  command_.getMessage().getQueryId());
8763  }
8764  else
8765  {
8766  // Persisted acks for writes don't come back with command id
8767  if (command == Message::Command::Publish ||
8768  command == Message::Command::DeltaPublish ||
8769  command == Message::Command::SOWDelete)
8770  {
8771  stream.setAcksOnly(commandID,
8772  ackTypes & (unsigned)~Message::AckType::Persisted);
8773  }
8774  else
8775  {
8776  stream.setAcksOnly(commandID, ackTypes);
8777  }
8778  }
8779  return stream;
8780  }
8781 
8782 // This is here because it uses api from Client.
8783  inline void Message::ack(const char* options_) const
8784  {
8785  ClientImpl* pClient = _body.get().clientImpl();
8786  Message::Field bookmark = getBookmark();
8787  if (pClient && bookmark.len() &&
8788  !pClient->getAutoAck())
8789  //(!pClient->getAutoAck() || getIgnoreAutoAck()))
8790  {
8791  pClient->ack(getTopic(), bookmark, options_);
8792  }
8793  }
8794 }// end namespace AMPS
8795 #endif
Command & setBookmark(const std::string &bookmark_)
Set the bookmark to be used this command.
Definition: ampsplusplus.hpp:717
Command & setFilter(const char *filter_, size_t filterLen_)
Definition: ampsplusplus.hpp:668
Class to hold string versions of failure reasons.
Definition: ampsplusplus.hpp:183
Message & setData(const std::string &v_)
Sets the data portion of self.
Definition: Message.hpp:1476
Core type and function declarations for the AMPS C client.
Client(const std::string &clientName="")
Constructs a new client with a given client name.
Definition: ampsplusplus.hpp:5072
Field getUserId() const
Retrieves the value of the UserId header of the Message as a new Field.
Definition: Message.hpp:1453
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&#39;s SOW cache.
Definition: ampsplusplus.hpp:6730
std::string stopTimer(const MessageHandler &messageHandler)
Definition: ampsplusplus.hpp:6704
static const unsigned int IdentifierLength
The length of identifiers used for unique identification of commands and subscriptions.
Definition: Message.hpp:542
std::string getAckType() const
Definition: ampsplusplus.hpp:915
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.
bool removeMessageHandler(const Field &commandId_)
Removes a MessageHandler for a given ComandId from self.
Definition: ampsplusplus.hpp:5284
Message & assignTopic(const std::string &v)
Sets the value of the Topic header for this Message.
Definition: Message.hpp:1449
Provides a convenient way of building messages in FIX format, typically referenced using the typedefs...
Definition: ampsplusplus.hpp:8066
void startTimer()
Definition: ampsplusplus.hpp:6693
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:6234
Abstract base class for storing published messages for an HA publisher client.
Definition: ampsplusplus.hpp:1059
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:8678
Field getSequence() const
Retrieves the value of the Sequence header of the Message as a new Field.
Definition: Message.hpp:1422
std::string send(const MessageHandler &messageHandler, Message &message, int timeout=0)
Sends a Message to the connected AMPS server, performing only minimal validation and bypassing client...
Definition: ampsplusplus.hpp:5312
AMPSDLL amps_result amps_client_set_name(amps_handle handle, const amps_char *clientName)
Sets the name on an amps client object.
Message & setCorrelationId(const std::string &v)
Sets the value of the CorrelationId header for this Message.
Definition: Message.hpp:1304
Message & setQueryID(const std::string &v)
Sets the value of the QueryID header for this Message.
Definition: Message.hpp:1417
Command(const std::string &command_)
Creates an object to represent the given AMPS command, such as "sow" or "subscribe".
Definition: ampsplusplus.hpp:529
Command::Type getCommandEnum() const
Decode self&#39;s "command" field and return one of the values from Command.
Definition: Message.hpp:1228
Command & setBookmark(const char *bookmark_, size_t bookmarkLen_)
Set the bookmark to be used this command.
Definition: ampsplusplus.hpp:728
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:6984
Command & setAckType(unsigned ackType_)
Definition: ampsplusplus.hpp:893
void setServerVersion(size_t version_)
Internally used to set the server version so the store knows how to deal with persisted acks and call...
Definition: BookmarkStore.hpp:429
void setDefaultMaxDepth(unsigned maxDepth_)
Sets a default max depth on all subsequently created MessageStream objects.
Definition: ampsplusplus.hpp:7352
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:5133
Field getSubscriptionId() const
Retrieves the value of the SubscriptionId header of the Message as a new Field.
Definition: Message.hpp:1427
AMPSDLL amps_result amps_client_connect(amps_handle handle, const amps_char *uri)
Connects to the AMPS server specified in uri.
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:5189
void unsubscribe(const std::string &commandId)
Unsubscribe from a topic.
Definition: ampsplusplus.hpp:6027
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:758
Message & setOrderBy(const std::string &v)
Sets the value of the OrderBy header for this Message.
Definition: Message.hpp:1415
Abstract base class for replaying a publish message.
Definition: ampsplusplus.hpp:1031
Command & setCommandId(const char *cmdId_, size_t cmdIdLen_)
Definition: ampsplusplus.hpp:642
void persisted(const Message::Field &subId_, const Message::Field &bookmark_)
Called internally to indicate messages up to and including bookmark are replicated to all replication...
Definition: BookmarkStore.hpp:405
unsigned getDefaultMaxDepth(void) const
Returns the default max depth for returned MessageStream objects.
Definition: ampsplusplus.hpp:7361
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:7220
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:5978
AMPSDLL void amps_client_disconnect(amps_handle handle)
Disconnects from the AMPS server, if connected.
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...
void discard(const Message::Field &subId_, size_t bookmarkSeqNo_)
Log a discard-bookmark entry to the persistent log based on a bookmark sequence number.
Definition: BookmarkStore.hpp:283
void setPublishStore(const Store &publishStore_)
Set the publish store to be used by the client.
Definition: ampsplusplus.hpp:5408
void setErrorOnPublishGap(bool errorOnPublishGap_)
Called to enable or disable throwing PublishStoreGapException.
Definition: ampsplusplus.hpp:1308
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:5673
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:5204
Message & setFilter(const std::string &v)
Sets the value of the Filter header for this Message.
Definition: Message.hpp:1306
Command & setSubId(const char *subId_, size_t subIdLen_)
Definition: ampsplusplus.hpp:694
void setNoEmpties(void)
Set the option to not send empty messages on a delta subscription.
Definition: Message.hpp:841
void setDuplicateMessageHandler(const MessageHandler &duplicateMessageHandler_)
Sets a callback function that is invoked when a duplicate message is detected.
Definition: ampsplusplus.hpp:5424
Message deepCopy(void) const
Returns a deep copy of self.
Definition: Message.hpp:579
void removeConnectionStateListener(ConnectionStateListener *listener)
Attempts to remove listener from self&#39;s set of ConnectionStateListeners.
Definition: ampsplusplus.hpp:7075
void setOOF(void)
Set the option to receive out of focus (OOF) messages on a subscription, where applicable.
Definition: Message.hpp:824
int getAckTimeout(void) const
Returns the current value of the message queue ack timeout setting – that is, the amount of time aft...
Definition: ampsplusplus.hpp:7304
VersionInfo getServerVersionInfo() const
Returns the server version retrieved during logon.
Definition: ampsplusplus.hpp:5175
Message sowDeleteByKeys(const std::string &topic_, const std::string &keys_, long timeout_=0)
Deletes messages that match SOW keys from a topic&#39;s SOW cache.
Definition: ampsplusplus.hpp:6757
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...
Command(const char *command_, size_t commandLen_)
Creates an object to represent the given AMPS command, such as "sow" or "subscribe".
Definition: ampsplusplus.hpp:537
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.
AMPSDLL amps_result amps_client_set_idle_time(amps_handle client, int idleTime)
Sets an idle-time (milliseconds).
Message encapsulates a single message sent to or received from an AMPS server, and provides methods f...
Definition: Message.hpp:531
Message & setAckTypeEnum(unsigned ackType_)
Encode self&#39;s "ack type" field from a bitmask of values from AckType.
Definition: Message.hpp:1183
amps_uint64_t getLastPersisted()
Get the last persisted message sequence in the store.
Definition: ampsplusplus.hpp:1279
std::string getString() const
Returns the current contents of this builder as a string.
Definition: ampsplusplus.hpp:8103
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:7372
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:5560
iterator begin(void)
Returns an iterator representing the beginning of the topic or subscription.
Definition: ampsplusplus.hpp:4976
Field getLeasePeriod() const
Retrieves the value of the LeasePeriod header of the Message as a new Field.
Definition: Message.hpp:1309
Command & setSowKeys(const char *sowKeys_, size_t sowKeysLen_)
Sets the SowKeys for the command.
Definition: ampsplusplus.hpp:629
static const char * NOW()
Convenience method for returning the special value to start a subscription at the end of the transact...
Definition: ampsplusplus.hpp:7003
Command & setAckType(const std::string &ackType_)
Definition: ampsplusplus.hpp:871
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:7013
void send(const Message &message)
Sends a Message to the connected AMPS server, performing only minimal validation and bypassing client...
Definition: ampsplusplus.hpp:5260
Message & setSowKey(const std::string &v)
Sets the value of the SowKey header for this Message.
Definition: Message.hpp:1424
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.
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:5950
Field getFilter() const
Retrieves the value of the Filter header of the Message as a new Field.
Definition: Message.hpp:1306
static size_t getUnsetPosition()
Method to return the value used to represent not found or unset.
Definition: ampsplusplus.hpp:1122
void setRetryOnDisconnect(bool isRetryOnDisconnect_)
Enables or disables automatic retry of a command to AMPS after a reconnect.
Definition: ampsplusplus.hpp:7334
unsigned getMaxDepth(void) const
Gets the maximum number of messages that can be held in the underlying queue.
Definition: ampsplusplus.hpp:8688
Message & assignUserId(const std::string &v)
Sets the value of the UserId header for this Message.
Definition: Message.hpp:1453
void connect(const std::string &uri)
Connect to an AMPS server.
Definition: ampsplusplus.hpp:5235
void clear()
Deletes the data associated with this Field, should only be used on Fields that were created as deepC...
Definition: Field.hpp:247
Abstract base class to manage all subscriptions placed on a client so that they can be re-established...
Definition: ampsplusplus.hpp:1423
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:6428
Success.
Definition: amps.h:205
Message & setCommandEnum(Command::Type command_)
Set self&#39;s "command" field from one of the values in Command.
Definition: Message.hpp:1288
Field getOptions() const
Retrieves the value of the Options header of the Message as a new Field.
Definition: Message.hpp:1316
std::string authenticate(const std::string &, const std::string &password_)
A simple implementation that returns an unmodified password.
Definition: ampsplusplus.hpp:1005
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:5650
FIXShredder(char fieldSep_=(char) 1)
Construct an instance of FIXShredder using the specified value as the delimiter between fields...
Definition: ampsplusplus.hpp:8148
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:6074
void * amps_handle
Opaque handle type used to refer to objects in the AMPS api.
Definition: amps.h:195
Class for constructing the options string to pass to AMPS in a Message.
Definition: Message.hpp:601
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:5273
const char * data() const
Returns the (non-null-terminated) data underlying this field.
Definition: Field.hpp:260
bool isValid() const
Returns true if self is a valid stream that may be iterated.
Definition: ampsplusplus.hpp:4969
amps_result
Return values from amps_xxx functions.
Definition: amps.h:200
FailedWriteHandler * getFailedWriteHandler()
Get the handler that is invoked to report on failed writes.
Definition: ampsplusplus.hpp:5461
Field getAckType() const
Retrieves the value of the AckType header of the Message as a new Field.
Definition: Message.hpp:1130
AMPSDLL amps_handle amps_client_create(const amps_char *clientName)
Functions for creation of an AMPS client.
Field getCommand() const
Retrieves the value of the Command header of the Message as a new Field.
Definition: Message.hpp:1195
MessageStream execute(Command &command_)
Execute the provided command and return messages received in response in a MessageStream.
Definition: ampsplusplus.hpp:8702
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:626
Message & setTopic(const std::string &v)
Sets the value of the Topic header for this Message.
Definition: Message.hpp:1449
Store getPublishStore()
Get the publish store used by the client.
Definition: ampsplusplus.hpp:5416
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:7386
unsigned getAckTypeEnum() const
Definition: ampsplusplus.hpp:920
size_t getServerVersion() const
Returns the server version retrieved during logon.
Definition: ampsplusplus.hpp:5164
State
Constants for the state of the connection.
Definition: ampsplusplus.hpp:1467
Command & setData(const std::string &data_)
Sets the data for this command from an existing string.
Definition: ampsplusplus.hpp:798
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:5722
void discardUpTo(amps_uint64_t index_)
Called by Client to indicate that all messages up to and including.
Definition: ampsplusplus.hpp:1212
Command & setFilter(const std::string &filter_)
Definition: ampsplusplus.hpp:661
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:5125
StoreImpl(bool errorOnPublishGap_=false)
Default constructor.
Definition: ampsplusplus.hpp:1067
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:6382
Client represents a connection to an AMPS server, but does not provide failover or reconnection behav...
Definition: ampsplusplus.hpp:5055
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:6959
Command & setSubId(const std::string &subId_)
Definition: ampsplusplus.hpp:687
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:6994
Message & newCommandId()
Creates and sets a new sequential value for the CommandId header for this Message.
Definition: Message.hpp:1302
void addConnectionStateListener(ConnectionStateListener *listener)
Adds a ConnectionStateListener to self&#39;s set of listeners.
Definition: ampsplusplus.hpp:7067
Command & reset(const std::string &command_)
Resets the fields of self, and sets the command to command_.
Definition: ampsplusplus.hpp:552
Command & setTimeout(unsigned timeout_)
Sets the client-side timeout for this command.
Definition: ampsplusplus.hpp:820
void clearConnectionStateListeners()
Clear all listeners from self&#39;s set of ConnectionStateListeners.
Definition: ampsplusplus.hpp:7082
Message & setSequence(const std::string &v)
Sets the value of the Sequence header for this Message.
Definition: Message.hpp:1422
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:5509
Command & setCommandId(const std::string &cmdId_)
Definition: ampsplusplus.hpp:635
Message & setUserId(const std::string &v)
Sets the value of the UserId header for this Message.
Definition: Message.hpp:1453
AMPSDLL amps_result amps_client_send_with_version(amps_handle client, amps_handle message, unsigned *version_out)
Sends a message to the AMPS server.
Field getTopic() const
Retrieves the value of the Topic header of the Message as a new Field.
Definition: Message.hpp:1449
AMPSDLL void amps_client_set_message_handler(amps_handle client, amps_handler messageHandler, void *userData)
Sets the message handler function for this client.
SubscriptionManager * getSubscriptionManager()
Get the subscription manager being used by the client.
Definition: ampsplusplus.hpp:5372
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:5785
Command(Message::Command::Type command_)
Creates an object to represent the given AMPS command, such as "sow" or "subscribe".
Definition: ampsplusplus.hpp:544
Message & setMessageType(const std::string &v)
Sets the value of the MessageType header for this Message.
Definition: Message.hpp:1312
Field getCommandId() const
Retrieves the value of the CommandId header of the Message as a new Field.
Definition: Message.hpp:1302
const ExceptionListener & getExceptionListener(void) const
Returns the exception listener set on this Client.
Definition: ampsplusplus.hpp:6870
Field getQueryId() const
Retrieves the value of the QueryID header of the Message as a new Field.
Definition: Message.hpp:1417
static Authenticator & instance()
Static function to return a static instance used when no Authenticator is supplied to a Client...
Definition: ampsplusplus.hpp:1022
Abstract base class for connection state listeners.
Definition: ampsplusplus.hpp:1463
Message & setSubscriptionId(const std::string &v)
Sets the value of the SubscriptionId header for this Message.
Definition: Message.hpp:1427
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:6792
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:766
Message & newQueryId()
Creates and sets a new sequential value for the QueryID header for this Message.
Definition: Message.hpp:1417
#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
Message & assignExpiration(const std::string &v)
Sets the value of the Expiration header for this Message.
Definition: Message.hpp:1305
bool empty() const
Returns &#39;true&#39; if empty, &#39;false&#39; otherwise.
Definition: Field.hpp:128
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:1233
void completed(const std::string &, const std::string &, const std::string &)
Called by Client once a logon completes successfully.
Definition: ampsplusplus.hpp:1017
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:5890
static const char * EPOCH()
Convenience method for returning the special value to start a subscription at the beginning of the tr...
Definition: ampsplusplus.hpp:7023
Interface for BookmarkStoreImpl classes.
Definition: BookmarkStore.hpp:228
Command & reset(const char *command_, size_t commandLen_)
Resets the fields of self, and sets the command to command_.
Definition: ampsplusplus.hpp:561
Command & setSequence(const char *seq_, size_t seqLen_)
Definition: ampsplusplus.hpp:779
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:5741
size_t len() const
Returns the length of the data underlying this field.
Definition: Field.hpp:267
virtual void completed(const std::string &userName_, const std::string &password_, const std::string &reason_)=0
Called by Client once a logon completes successfully.
Message & setClientName(const std::string &v)
Sets the value of the ClientName header for this Message.
Definition: Message.hpp:1303
Field getSowKey() const
Retrieves the value of the SowKey header of the Message as a new Field.
Definition: Message.hpp:1424
#define AMPS_BOOKMARK_EPOCH
Start the subscription at the beginning of the journal.
Definition: BookmarkStore.hpp:51
A default implementation of Authenticator that only uses an unchanged password and does not implement...
Definition: ampsplusplus.hpp:999
void setLogonCorrelationData(const std::string &logonCorrelationData_)
Sets the logon correlation data for the client.
Definition: ampsplusplus.hpp:5144
Message & setCommand(const std::string &v)
Sets the value of the Command header for this Message.
Definition: Message.hpp:1195
_FIXBuilder(char fieldSep_=(char) 1)
Construct an instance of _FIXBuilder, using the specified separator between fields.
Definition: ampsplusplus.hpp:8076
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:7232
std::string retry(const std::string &, const std::string &)
Throws an AuthenticationException because retry is not implemented.
Definition: ampsplusplus.hpp:1012
Message & assignSubscriptionId(const std::string &v)
Sets the value of the SubscriptionId header for this Message.
Definition: Message.hpp:1427
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:1053
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.
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:6272
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:1153
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:8085
Message & assignCommand(const std::string &v)
Sets the value of the Command header for this Message.
Definition: Message.hpp:1195
#define AMPS_BOOKMARK_NOW
Start the subscription at the point in time when AMPS processes the subscription. ...
Definition: BookmarkStore.hpp:55
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:1293
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.
BookmarkStore getBookmarkStore()
Get the bookmark store being used by the client.
Definition: ampsplusplus.hpp:5364
bool isValid() const
Method to return if there is an underlying implementation for the Store.
Definition: ampsplusplus.hpp:1250
void setAckBatchSize(const unsigned ackBatchSize_)
Sets the queue ack batch size setting.
Definition: ampsplusplus.hpp:7293
Field getPassword() const
Retrieves the value of the Password header of the Message as a new Field.
Definition: Message.hpp:1416
Message & setTopNRecordsReturned(const std::string &v)
Sets the value of the TopNRecordsReturned header for this Message.
Definition: Message.hpp:1451
Class to handle when a client receives a duplicate publish message, or not entitled message...
Definition: ampsplusplus.hpp:1343
Message sowDelete(const std::string &topic, const std::string &filter, long timeout=0)
Deletes one or more messages from a topic&#39;s SOW cache.
Definition: ampsplusplus.hpp:6668
virtual void setFailedResubscribeHandler(std::shared_ptr< FailedResubscribeHandler > handler_)
Set a handler to deal with failing subscriptions after a failover event.
Definition: ampsplusplus.hpp:1451
Message & setSowKeys(const std::string &v)
Sets the value of the SowKeys header for this Message.
Definition: Message.hpp:1425
void setBookmarkStore(const BookmarkStore &bookmarkStore_)
Set the bookmark store to be used by the client.
Definition: ampsplusplus.hpp:5356
This class multiplexes messages from AMPS to multiple subscribers and uses the stream of acks from AM...
Definition: MessageRouter.hpp:149
Message & setExpiration(const std::string &v)
Sets the value of the Expiration header for this Message.
Definition: Message.hpp:1305
virtual std::string authenticate(const std::string &userName_, const std::string &password_)=0
Called by Client just before the logon command is sent.
Command & setOrderBy(const std::string &orderBy_)
Definition: ampsplusplus.hpp:674
void setExceptionListener(const ExceptionListener &listener_)
Definition: ampsplusplus.hpp:6863
Command & reset(Message::Command::Type command_)
Resets the fields of self, and sets the command to command_.
Definition: ampsplusplus.hpp:569
void setSubscriptionManager(SubscriptionManager *subscriptionManager_)
Set the subscription manager to be used by the client.
Definition: ampsplusplus.hpp:5384
void setUnhandledMessageHandler(const AMPS::MessageHandler &messageHandler)
Definition: ampsplusplus.hpp:6926
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:8096
unsigned getAckBatchSize(void) const
Returns the value of the queue ack batch size setting.
Definition: ampsplusplus.hpp:7283
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:5627
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:6359
Command & setExpiration(unsigned expiration_)
Set the expiration time for a publish command.
Definition: ampsplusplus.hpp:851
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.
Represents an iterator over messages in an AMPS topic.
Definition: ampsplusplus.hpp:4931
size_t log(Message &message_)
Log a bookmark to the persistent log.
Definition: BookmarkStore.hpp:268
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:6606
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:1242
Command & setData(const char *data_, size_t dataLen_)
Sets the data for this command.
Definition: ampsplusplus.hpp:806
Message & assignAckType(const std::string &v)
Sets the value of the AckType header for this Message.
Definition: Message.hpp:1130
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:5606
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:7112
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:594
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:6565
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:5760
void unsubscribe()
Unsubscribe from all topics.
Definition: ampsplusplus.hpp:6039
void setAutoAck(bool isAutoAckEnabled_)
Sets the queue auto-ack setting on this client.
Definition: ampsplusplus.hpp:7275
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:5908
Message & assignCorrelationId(const std::string &v)
Sets the value of the CorrelationId header for this Message.
Definition: Message.hpp:1304
amps_uint64_t store(const Message &message_)
Called by Client to store a message being published.
Definition: ampsplusplus.hpp:1201
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:6529
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:835
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:6850
StoreImpl * get()
Used to get a pointer to the implementation.
Definition: ampsplusplus.hpp:1325
MessageHandler getDuplicateMessageHandler(void)
Returns the callback function that is invoked when a duplicate message is detected.
Definition: ampsplusplus.hpp:5439
Command & setQueryId(const char *queryId_, size_t queryIdLen_)
Definition: ampsplusplus.hpp:707
DisconnectHandler getDisconnectHandler(void) const
Returns the callback function that is invoked when a disconnect occurs.
Definition: ampsplusplus.hpp:5334
Class for parsing a FIX format message into a std::map of keys and values, where the keys and values ...
Definition: ampsplusplus.hpp:8140
Field represents the value of a single field in a Message.
Definition: Field.hpp:86
Message & setAckType(const std::string &v)
Sets the value of the AckType header for this Message.
Definition: Message.hpp:1130
virtual ConnectionInfo getConnectionInfo() const
Get the connection information for the current connection.
Definition: ampsplusplus.hpp:5343
Message & setOptions(const std::string &v)
Sets the value of the Options header for this Message.
Definition: Message.hpp:1344
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:7208
Abstract base class where you can implement handling of exceptions that occur when a SubscriptionMana...
Definition: ampsplusplus.hpp:1401
AMPSDLL amps_result amps_client_send(amps_handle client, amps_handle message)
Sends a message to the AMPS server.
std::map< Message::Field, Message::Field > map_type
Convenience defintion for the std::map specialization used for this class.
Definition: ampsplusplus.hpp:8152
Command & setTopic(const char *topic_, size_t topicLen_)
Definition: ampsplusplus.hpp:655
void setDisconnectHandler(const DisconnectHandler &disconnectHandler)
Sets the function to be called when the client is unintentionally disconnected.
Definition: ampsplusplus.hpp:5326
Handle class for StoreImpl classes that track publish messages.
Definition: ampsplusplus.hpp:1185
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:8159
Exception listener for unhandled exceptions.
Definition: ampsplusplus.hpp:236
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:6461
void setHeartbeat(unsigned heartbeatTime_)
Requests heartbeating with the AMPS server.
Definition: ampsplusplus.hpp:6920
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:5843
An iterable object representing the results of an AMPS subscription and/or query. ...
Definition: ampsplusplus.hpp:4923
bool getErrorOnPublishGap() const
Called to check if the Store will throw PublishStoreGapException.
Definition: ampsplusplus.hpp:1317
Command & setSequence(const std::string &seq_)
Definition: ampsplusplus.hpp:772
static const char * MOST_RECENT()
Convenience method for returning the special value to start a subscription at a recovery point based ...
Definition: ampsplusplus.hpp:7045
Command & setQueryId(const std::string &queryId_)
Definition: ampsplusplus.hpp:700
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...
Message & setCommandId(const std::string &v)
Sets the value of the CommandId header for this Message.
Definition: Message.hpp:1302
Command & setSequence(const amps_uint64_t seq_)
Definition: ampsplusplus.hpp:785
static const char * BOOKMARK_RECENT()
Convenience method for returning the special value to start a subscription at a recovery point based ...
Definition: ampsplusplus.hpp:7056
Message & setQueryId(const std::string &v)
Sets the value of the QueryID header for this Message.
Definition: Message.hpp:1417
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:652
amps_uint64_t getLowestUnpersisted()
Get the oldest unpersisted message sequence in the store.
Definition: ampsplusplus.hpp:1271
Command & setTopic(const std::string &topic_)
Definition: ampsplusplus.hpp:648
MessageStream maxDepth(unsigned maxDepth_)
Sets the maximum number of messages that can be held in the underlying queue.
Definition: ampsplusplus.hpp:8683
Message & assignVersion(const std::string &v)
Sets the value of the Version header for this Message.
Definition: Message.hpp:1452
void disconnect()
Disconnect from an AMPS server.
Definition: ampsplusplus.hpp:5242
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:6112
unsigned getDepth(void) const
Gets the current number of messages held in the underlying queue.
Definition: ampsplusplus.hpp:8692
Field getQueryID() const
Retrieves the value of the QueryID header of the Message as a new Field.
Definition: Message.hpp:1417
The operation has not succeeded, but ought to be retried.
Definition: amps.h:229
const std::string & getLogonCorrelationData() const
Returns the currently set logon correlation data for the client.
Definition: ampsplusplus.hpp:5151
void setHeartbeat(unsigned heartbeatTime_, unsigned readTimeout_)
Requests heartbeating with the AMPS server.
Definition: ampsplusplus.hpp:6896
Command & setTopN(unsigned topN_)
Definition: ampsplusplus.hpp:826
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...
const std::string & getURI() const
Returns the last URI this client is connected to.
Definition: ampsplusplus.hpp:5211
Command & addAckType(const std::string &ackType_)
Definition: ampsplusplus.hpp:857
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:1221
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:7034
Message & setBookmark(const std::string &v)
Sets the value of the Bookmark header for this Message.
Definition: Message.hpp:1194
void deepCopy(const Field &orig_)
Makes self a deep copy of the original field.
Definition: Field.hpp:218
Command & setOrderBy(const char *orderBy_, size_t orderByLen_)
Definition: ampsplusplus.hpp:681
unsigned getAckTypeEnum() const
Decode self&#39;s "ack type" field and return the corresponding bitmask of values from AckType...
Definition: Message.hpp:1160
BookmarkStoreImpl * get()
Used to get a pointer to the implementation.
Definition: BookmarkStore.hpp:465
amps_handle getHandle()
Returns the underlying amps_handle for this client, to be used with amps_client_* functions from the ...
Definition: ampsplusplus.hpp:6837
AMPSDLL amps_result amps_client_attempt_reconnect(amps_handle client, unsigned version)
Manually invokes the user-supplied disconnect handler for this client.
AMPSDLL void amps_client_destroy(amps_handle handle)
Disconnects and destroys an AMPS client object.
void reset()
Clear all data from the builder.
Definition: ampsplusplus.hpp:8113
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:5453
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:6484
void setAckTimeout(const int ackTimeout_)
Sets the message queue ack timeout value.
Definition: ampsplusplus.hpp:7316
Command & setSowKeys(const std::string &sowKeys_)
Sets the SowKeys for the command.
Definition: ampsplusplus.hpp:611
Message & setPassword(const std::string &v)
Sets the value of the Password header for this Message.
Definition: Message.hpp:1416
bool getAutoAck(void) const
Returns the value of the queue auto-ack setting.
Definition: ampsplusplus.hpp:7265
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:5484
Definition: ampsplusplus.hpp:106
void setName(const std::string &name)
Sets the name of this client, assuming no name was provided previously.
Definition: ampsplusplus.hpp:5110
The interface for handling authentication with the AMPS server.
Definition: ampsplusplus.hpp:968
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:1263
Field getBookmark() const
Retrieves the value of the Bookmark header of the Message as a new Field.
Definition: Message.hpp:1194
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:1373
static amps_uint64_t getUnsetSequence()
Method to return the value used to represent no such sequence.
Definition: ampsplusplus.hpp:1129
Command & setCorrelationId(const char *correlationId_, size_t correlationIdLen_)
Set the correlation ID for this command.
Definition: ampsplusplus.hpp:751
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:6131
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:5533
void setLastChanceMessageHandler(const AMPS::MessageHandler &messageHandler)
Sets the message handler called when no other handler matches.
Definition: ampsplusplus.hpp:6933
Message::Field getMostRecent(const Message::Field &subId_)
Returns the most recent bookmark from the log that ought to be used for (re-)subscriptions.
Definition: BookmarkStore.hpp:310
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:5699
The client and server are disconnected.
Definition: amps.h:233
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:6813
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:5872
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:8673
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:6170
const std::string & getName() const
Returns the name of this client passed in the constructor.
Definition: ampsplusplus.hpp:5117
Command is an encapsulation of a single AMPS command sent by the client.
Definition: ampsplusplus.hpp:441
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:6320
Message & setBatchSize(const std::string &v)
Sets the value of the BatchSize header for this Message.
Definition: Message.hpp:1193
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:5811
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:5999
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:581
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:7146
Command & setCorrelationId(const std::string &correlationId_)
Set the correlation ID for this command.
Definition: ampsplusplus.hpp:739
std::string sowDelete(const MessageHandler &messageHandler, const std::string &topic, const std::string &filter, long timeout)
Deletes one or more messages from a topic&#39;s SOW cache.
Definition: ampsplusplus.hpp:6645
iterator end(void)
Returns an iterator representing the end of the topic or subscription.
Definition: ampsplusplus.hpp:4987
bool getRetryOnDisconnect(void) const
Returns true if automatic retry of a command to AMPS after a reconnect is enabled.
Definition: ampsplusplus.hpp:7343
void flushAcks(void)
Sends any queued message queue ack messages to the server immediately.
Definition: ampsplusplus.hpp:7256
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:6202