AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.4.3
HAClientImpl.hpp
1 //
3 // Copyright (c) 2010-2024 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 
26 #ifndef _HACLIENTIMPL_H_
27 #define _HACLIENTIMPL_H_
28 
29 #include <typeinfo>
30 #include <amps/ampsplusplus.hpp>
31 #include <amps/ServerChooser.hpp>
34 #if __cplusplus >= 201103L || _MSC_VER >= 1900
35  #include <atomic>
36 #endif
37 
38 namespace AMPS
39 {
40 
41  class HAClientImpl : public ClientImpl
42  {
43  public:
44  HAClientImpl(const std::string& name_)
45  : ClientImpl(name_), _timeout(AMPS_HACLIENT_TIMEOUT_DEFAULT)
46  , _reconnectDelay(AMPS_HACLIENT_RECONNECT_DEFAULT)
47  , _reconnectDelayStrategy(new ExponentialDelayStrategy(_reconnectDelay))
48  , _disconnected(false)
49  {
50 #ifdef AMPS_USE_FUNCTIONAL
51  setDisconnectHandler(HADisconnectHandler());
52 #else
53  setDisconnectHandler(AMPS::DisconnectHandler(&HADisconnectHandler::invoke, NULL));
54 #endif
55  setSubscriptionManager(new MemorySubscriptionManager());
56  }
57 
58  ~HAClientImpl()
59  {
60  _disconnected = true;
61  _cleanup();
62  }
63 
64  void setTimeout(int timeout_)
65  {
66  _timeout = timeout_;
67  }
68 
69  int getTimeout() const
70  {
71  return _timeout;
72  }
73 
74  unsigned int getReconnectDelay(void) const
75  {
76  return _reconnectDelay;
77  }
78 
79  void setReconnectDelay(unsigned int reconnectDelay_)
80  {
81  _reconnectDelay = reconnectDelay_;
82  setReconnectDelayStrategy(new FixedDelayStrategy(
83  (unsigned int)reconnectDelay_));
84  }
85 
86  void setReconnectDelayStrategy(const ReconnectDelayStrategy& strategy_)
87  {
88  _reconnectDelayStrategy = strategy_;
89  }
90 
91  ReconnectDelayStrategy getReconnectDelayStrategy(void) const
92  {
93  return _reconnectDelayStrategy;
94  }
95 
96  std::string getLogonOptions(void) const
97  {
98  return _logonOptions;
99  }
100 
101  void setLogonOptions(const std::string& logonOptions_)
102  {
103  _logonOptions = logonOptions_;
104  }
105 
106  void setLogonOptions(const char* logonOptions_)
107  {
108  _logonOptions = logonOptions_;
109  }
110 
111  ServerChooser getServerChooser() const
112  {
113  return _serverChooser;
114  }
115 
116  void setServerChooser(const ServerChooser& serverChooser_)
117  {
118  _serverChooser = serverChooser_;
119  }
120 
121  class HADisconnectHandler
122  {
123  public:
124  HADisconnectHandler() {}
125  static void invoke(Client& client, void* );
126 #ifdef AMPS_USE_FUNCTIONAL
127  void operator()(Client& client)
128  {
129  invoke(client, NULL);
130  }
131 #endif
132  };
133  void connectAndLogon()
134  {
135  Lock<Mutex> l(_connectAndLogonLock);
136  // AC-1030 In case this is called on a client after delay strategy caused a failure.
137  _reconnectDelayStrategy.reset();
138  try
139  {
140  while (true)
141  {
142  _disconnected = false;
143  connectAndLogonInternal();
144  try
145  {
146  // Resubscribe
147  if (_subscriptionManager)
148  {
149  Client c(this);
150  _subscriptionManager->resubscribe(c);
151  broadcastConnectionStateChanged(
152  ConnectionStateListener::Resubscribed);
153  }
154  return;
155  }
156  catch (const AMPSException& subEx)
157  {
158  // Keep receive thread from reconnecting
159  _disconnected = true;
160  _serverChooser.reportFailure(subEx, getConnectionInfo());
161  ClientImpl::setDisconnected();
162  }
163  }
164  }
165  catch (...)
166  {
167  // Failure, make sure we're disconnected
168  disconnect();
169  throw;
170  }
171  }
172 
173  virtual void connect(const std::string& /*uri*/)
174  {
175  connectAndLogon();
176  }
177 
178  virtual std::string logon(long /*timeout_*/, Authenticator& /*authenticator_*/,
179  const char* /*options_*/)
180  {
181  if (_disconnected)
182  {
183  throw DisconnectedException("Attempt to call logon on a disconnected HAClient. Use connectAndLogon() instead.");
184  }
185  throw AlreadyConnectedException("Attempt to call logon on an HAClient. Use connectAndLogon() instead.");
186  }
187 
188  static void HADoNothingDisconnectHandler(amps_handle /*client*/,
189  void* /*data*/)
190  {
191  ;
192  }
193 
194  class DisconnectHandlerDisabler
195  {
196  public:
197  DisconnectHandlerDisabler()
198  : _pClient(NULL), _queueAckTimeout(0) { }
199  DisconnectHandlerDisabler(HAClientImpl* pClient_)
200  : _pClient(pClient_)
201  {
202  setHandler();
203  _queueAckTimeout = _pClient->getAckTimeout();
204  _pClient->setAckTimeout(0);
205  }
206  ~DisconnectHandlerDisabler()
207  {
208  clear();
209  }
210  void clear()
211  {
212  if (_pClient)
213  {
215  _pClient->getHandle(),
216  (amps_handler)ClientImpl::ClientImplDisconnectHandler,
217  _pClient);
218  if (_queueAckTimeout)
219  {
220  _pClient->setAckTimeout(_queueAckTimeout);
221  _queueAckTimeout = 0;
222  }
223  _pClient = NULL;
224  }
225  }
226  void setClient(HAClientImpl* pClient_)
227  {
228  if (!_pClient)
229  {
230  _pClient = pClient_;
231  setHandler();
232  _queueAckTimeout = _pClient->getAckTimeout();
233  _pClient->setAckTimeout(0);
234  amps_client_disconnect(_pClient->getHandle());
235  }
236  }
237  void setHandler()
238  {
240  _pClient->getHandle(),
241  (amps_handler)HAClientImpl::HADoNothingDisconnectHandler,
242  _pClient);
243  }
244  private:
245  HAClientImpl* _pClient;
246  int _queueAckTimeout;
247  };
248 
249  void connectAndLogonInternal()
250  {
251  if (!_serverChooser.isValid())
252  {
253  throw ConnectionException("No server chooser registered with HAClient");
254  }
255  {
256  DisconnectHandlerDisabler disconnectDisabler;
257  while (!_disconnected)
258  {
259  std::string uri = _serverChooser.getCurrentURI();
260  if (uri.empty())
261  {
262  throw ConnectionException("No AMPS instances available for connection. " + _serverChooser.getError());
263  }
264  Authenticator& auth = _serverChooser.getCurrentAuthenticator();
265  // Begin locked section - see AC-1017
266  Lock<Mutex> l(_connectLock);
267  _sleepBeforeConnecting(uri);
268  try
269  {
270  // Check if another thread disconnected or already connected
271  if (_disconnected || _connected)
272  {
273  return;
274  }
275  // Temporarily unset the disconnect handler since we will loop
276  disconnectDisabler.setClient((HAClientImpl*)this);
277  // Connect and logon while holding the _lock
278  {
279  Lock<Mutex> clientLock(_lock);
280  ClientImpl::_connect(uri);
281  try
282  {
283  if (_logonOptions.empty())
284  {
285  ClientImpl::_logon(_timeout, auth);
286  }
287  else
288  {
289  ClientImpl::_logon(_timeout, auth, _logonOptions.c_str());
290  }
291  } // All of the following may not disconnect the client
292  catch (const AuthenticationException&)
293  {
294  ClientImpl::setDisconnected();
295  throw;
296  }
297  catch (const NotEntitledException&)
298  {
299  ClientImpl::setDisconnected();
300  throw;
301  }
302  catch (const DuplicateLogonException&)
303  {
304  ClientImpl::setDisconnected();
305  throw;
306  }
307  catch (const NameInUseException&)
308  {
309  ClientImpl::setDisconnected();
310  throw;
311  }
312  catch (const TimedOutException&)
313  {
314  ClientImpl::setDisconnected();
315  throw;
316  }
317  }
318  disconnectDisabler.clear();
319  try
320  {
321  _serverChooser.reportSuccess(getConnectionInfo());
322  _reconnectDelayStrategy.reset();
323  }
324  catch (const AMPSException&)
325  {
326  ClientImpl::disconnect();
327  throw;
328  }
329  break;
330  }
331  catch (const AMPSException& ex)
332  {
333  Unlock<Mutex> u(_connectLock);
334  ConnectionInfo ci = getConnectionInfo();
335  // Substitute the URI on the connection info with the one we attempted
336  ci["client.uri"] = uri;
337  _serverChooser.reportFailure(ex, ci);
338  try
339  {
340  ClientImpl::setDisconnected();
341  }
342  catch (const std::exception& e)
343  {
344  try
345  {
346  _exceptionListener->exceptionThrown(e);
347  }
348  catch (...) { } // -V565
349  }
350  catch (...)
351  {
352  try
353  {
354  _exceptionListener->exceptionThrown(UnknownException("Unknown exception calling setDisconnected"));
355  }
356  catch (...) { } // -V565
357  }
358  }
359  }
360  }
361  return;
362  }
363 
364  ConnectionInfo gatherConnectionInfo() const
365  {
366  return getConnectionInfo();
367  }
368 
369  ConnectionInfo getConnectionInfo() const
370  {
371  ConnectionInfo info = ClientImpl::getConnectionInfo();
372  std::ostringstream writer;
373 
374  writer << getReconnectDelay();
375  info["haClient.reconnectDelay"] = writer.str();
376  writer.clear(); writer.str("");
377  writer << _timeout;
378  info["haClient.timeout"] = writer.str();
379 
380  return info;
381  }
382 
383  bool disconnected() const
384  {
385  return _disconnected;
386  }
387  private:
388 
389  void disconnect()
390  {
391  {
392  Lock<Mutex> l(_connectLock);
393  _disconnected = true;
394  }
395  ClientImpl::disconnect();
396  }
397  void _millisleep(unsigned int millis_)
398  {
399  if (millis_ == 0)
400  {
401  return;
402  }
403  double waitTime = (double)millis_;
404  Timer timer(waitTime);
405  timer.start();
406  while (!timer.checkAndGetRemaining(&waitTime))
407  {
408  if (waitTime - 1000.0 > 0.0)
409  {
410  AMPS_USLEEP(1000000);
411  }
412  else
413  {
414  AMPS_USLEEP(1000UL * (unsigned int)waitTime);
415  }
416  amps_invoke_waiting_function();
417  }
418  }
419  void _sleepBeforeConnecting(const std::string& uri_)
420  {
421  try
422  {
423  _millisleep(
424  _reconnectDelayStrategy.getConnectWaitDuration(uri_));
425  }
426  catch (const ConnectionException&)
427  {
428  throw;
429  }
430  catch (const std::exception& ex_)
431  {
432  _exceptionListener->exceptionThrown(ex_);
433  throw ConnectionException(ex_.what());
434  }
435  catch (...)
436  {
437  throw ConnectionException("Unknown exception thrown by "
438  "the HAClient's delay strategy.");
439  }
440  }
441 
442  Mutex _connectLock;
443  Mutex _connectAndLogonLock;
444  int _timeout;
445  unsigned int _reconnectDelay;
446  ReconnectDelayStrategy _reconnectDelayStrategy;
447  ServerChooser _serverChooser;
448 #if __cplusplus >= 201103L || _MSC_VER >= 1900
449  std::atomic<bool> _disconnected;
450 #else
451  volatile bool _disconnected;
452 #endif
453  std::string _logonOptions;
454 
455  }; // class HAClientImpl
456 
457 }// namespace AMPS
458 
459 #endif //_HACLIENTIMPL_H_
460 
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.
AMPSDLL void amps_client_disconnect(amps_handle handle)
Disconnects from the AMPS server, if connected.
Provides AMPS::MemorySubscriptionManager, used by an AMPS::HAClient to resubmit subscriptions if conn...
void * amps_handle
Opaque handle type used to refer to objects in the AMPS api.
Definition: amps.h:211
Core type, function, and class declarations for the AMPS C++ client.
Provides AMPS::ReconnectDelayStrategy, called by an AMPS::HAClient to determine how long to wait betw...
Provides AMPS::ServerChooser, the abstract base class that defines the interface that an AMPS::HAClie...
Definition: ampsplusplus.hpp:102