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