AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.0.5
HAClientImpl.hpp
1 //
3 // Copyright (c) 2010-2020 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 <ampsplusplus.hpp>
31 #include <ServerChooser.hpp>
34 
35 namespace AMPS
36 {
37 
38 class HAClientImpl : public ClientImpl
39 {
40 public:
41  HAClientImpl(const std::string& name_)
42  : ClientImpl(name_), _timeout(AMPS_HACLIENT_TIMEOUT_DEFAULT)
43  , _reconnectDelay(AMPS_HACLIENT_RECONNECT_DEFAULT)
44  , _reconnectDelayStrategy(new ExponentialDelayStrategy(_reconnectDelay))
45  , _disconnected(false)
46  {
47 #ifdef AMPS_USE_FUNCTIONAL
48  setDisconnectHandler(HADisconnectHandler());
49 #else
50  setDisconnectHandler(AMPS::DisconnectHandler(&HADisconnectHandler::invoke, NULL));
51 #endif
52  setSubscriptionManager(new MemorySubscriptionManager());
53  }
54 
55  ~HAClientImpl()
56  {
57  _disconnected = true;
58  _cleanup();
59  }
60 
61  void setTimeout(int timeout_)
62  {
63  _timeout = timeout_;
64  }
65 
66  int getTimeout() const
67  {
68  return _timeout;
69  }
70 
71  unsigned int getReconnectDelay(void) const
72  {
73  return _reconnectDelay;
74  }
75 
76  void setReconnectDelay(unsigned int reconnectDelay_)
77  {
78  _reconnectDelay = reconnectDelay_;
79  setReconnectDelayStrategy(new FixedDelayStrategy(
80  (unsigned int)reconnectDelay_));
81  }
82 
83  void setReconnectDelayStrategy(const ReconnectDelayStrategy& strategy_)
84  {
85  _reconnectDelayStrategy = strategy_;
86  }
87 
88  ReconnectDelayStrategy getReconnectDelayStrategy(void) const
89  {
90  return _reconnectDelayStrategy;
91  }
92 
93  std::string getLogonOptions(void) const
94  {
95  return _logonOptions;
96  }
97 
98  void setLogonOptions(const std::string& logonOptions_)
99  {
100  _logonOptions = logonOptions_;
101  }
102 
103  void setLogonOptions(const char* logonOptions_)
104  {
105  _logonOptions = logonOptions_;
106  }
107 
108  ServerChooser getServerChooser() const
109  {
110  return _serverChooser;
111  }
112 
113  void setServerChooser(const ServerChooser& serverChooser_)
114  {
115  _serverChooser = serverChooser_;
116  }
117 
118  class HADisconnectHandler
119  {
120  public:
121  HADisconnectHandler() {}
122  static void invoke(Client& client, void* );
123 #ifdef AMPS_USE_FUNCTIONAL
124  void operator()(Client& client)
125  {
126  invoke(client, NULL);
127  }
128 #endif
129  };
130  void connectAndLogon()
131  {
132  Lock<Mutex> l(_connectAndLogonLock);
133  _disconnected = false;
134  while(true)
135  {
136  connectAndLogonInternal();
137  try
138  {
139  // Resubscribe
140  if(_subscriptionManager)
141  {
142  Client c(this);
143  _subscriptionManager->resubscribe(c);
144  broadcastConnectionStateChanged(
145  ConnectionStateListener::Resubscribed);
146  }
147  return;
148  }
149  catch(const AMPSException& subEx)
150  {
151  _serverChooser.reportFailure(subEx, getConnectionInfo());
152  }
153  }
154  }
155 
156  virtual void connect(const std::string& /*uri*/)
157  {
158  connectAndLogon();
159  }
160 
161  virtual std::string logon(long /*timeout_*/, Authenticator& /*authenticator_*/,
162  const char* /*options_*/)
163  {
164  if (_disconnected) throw DisconnectedException("Attempt to call logon on a disconnected HAClient. Use connectAndLogon() instead.");
165  throw AlreadyConnectedException("Attempt to call logon on an HAClient. Use connectAndLogon() instead.");
166  }
167 
168  class HADoNothingDisconnectHandler
169  {
170  public:
171  HADoNothingDisconnectHandler() {}
172  static void invoke(Client& , void* ) {;}
173 #ifdef AMPS_USE_FUNCTIONAL
174  void operator()(Client& client)
175  {
176  invoke(client, NULL);
177  }
178 #endif
179  };
180 
181  class DisconnectHandlerDisabler
182  {
183  public:
184  DisconnectHandlerDisabler() : _client(NULL) { }
185  DisconnectHandlerDisabler(HAClientImpl* client_)
186  : _client(client_)
187  {
188  _handler = _client->getDisconnectHandler();
189  setHandler();
190  }
191  ~DisconnectHandlerDisabler() { clear(); }
192  void clear()
193  {
194  if(_client)
195  {
196  _client->setDisconnectHandler(_handler);
197  _client = NULL;
198  }
199  }
200  void setClient(HAClientImpl* client_)
201  {
202  if (!_client)
203  {
204  _client = client_;
205  _handler = _client->getDisconnectHandler();
206  setHandler();
207  }
208  }
209  void setHandler()
210  {
211 #ifdef AMPS_USE_FUNCTIONAL
212  _client->setDisconnectHandler(HADoNothingDisconnectHandler());
213 #else
214  _client->setDisconnectHandler(DisconnectHandler(
215  &HADoNothingDisconnectHandler::invoke,
216  NULL));
217 #endif
218  }
219  private:
220  HAClientImpl* _client;
221  DisconnectHandler _handler;
222  };
223 
224  void connectAndLogonInternal()
225  {
226  if(!_serverChooser.isValid())
227  {
228  throw ConnectionException("No server chooser registered with HAClient");
229  }
230  {
231  DisconnectHandlerDisabler disconnectDisabler;
232  while(!_disconnected)
233  {
234  std::string uri = _serverChooser.getCurrentURI();
235  if(uri.empty())
236  {
237  throw ConnectionException("No AMPS instances available for connection. " + _serverChooser.getError());
238  }
239  Authenticator& auth = _serverChooser.getCurrentAuthenticator();
240  _sleepBeforeConnecting(uri);
241  try
242  {
243  // Begin locked section
244  Lock<Mutex> l(_connectLock);
245  // Check if another thread disconnected or already connected
246  if(_disconnected || _connected)
247  {
248  return;
249  }
250  // Temporarily unset the disconnect handler since we will loop
251  disconnectDisabler.setClient((HAClientImpl*)this);
252  // Connect
253  ClientImpl::connect(uri);
254  if (_logonOptions.empty())
255  {
256  ClientImpl::logon(_timeout, auth);
257  }
258  else
259  {
260  ClientImpl::logon(_timeout, auth, _logonOptions.c_str());
261  }
262  disconnectDisabler.clear();
263  try
264  {
265  _serverChooser.reportSuccess(getConnectionInfo());
266  _reconnectDelayStrategy.reset();
267  }
268  catch(const AMPSException& e)
269  {
270  ClientImpl::disconnect();
271  throw AMPSException(e);
272  }
273  break;
274  }
275  catch(const AMPSException& ex)
276  {
277  ConnectionInfo ci = getConnectionInfo();
278  // Substitute the URI on the connection info with the one we attempted
279  ci["client.uri"] = uri;
280  _serverChooser.reportFailure(ex,ci);
281  try
282  {
283  ClientImpl::setDisconnected();
284  }
285  catch (const AMPSException& e)
286  {
287  try
288  {
289  _exceptionListener->exceptionThrown(e);
290  } catch (...) { }
291  }
292  }
293  }
294  }
295  return;
296  }
297 
298  ConnectionInfo gatherConnectionInfo() const
299  {
300  return getConnectionInfo();
301  }
302 
303  ConnectionInfo getConnectionInfo() const
304  {
305  ConnectionInfo info = ClientImpl::getConnectionInfo();
306  std::ostringstream writer;
307 
308  writer << getReconnectDelay();
309  info["haClient.reconnectDelay"] = writer.str();
310  writer.clear(); writer.str("");
311  writer << _timeout;
312  info["haClient.timeout"] = writer.str();
313 
314  return info;
315  }
316 
317  bool disconnected() const
318  {
319  return _disconnected;
320  }
321  private:
322 
323  void disconnect()
324  {
325  {
326  Lock<Mutex> l(_connectLock);
327  _disconnected = true;
328  }
329  ClientImpl::disconnect();
330  }
331  void _millisleep(unsigned int millis_)
332  {
333 #ifdef _WIN32
334  Sleep(millis_);
335 #else
336  struct timespec ts;
337  ts.tv_sec = millis_ / 1000;
338  ts.tv_nsec = millis_ % 1000 * 1000000;
339  nanosleep(&ts, NULL);
340 #endif
341  }
342  void _sleepBeforeConnecting(const std::string& uri_)
343  {
344  try
345  {
346  _millisleep(
347  _reconnectDelayStrategy.getConnectWaitDuration(uri_));
348  }
349  catch(const ConnectionException&)
350  {
351  throw;
352  }
353  catch(const std::exception& ex_)
354  {
355  _exceptionListener->exceptionThrown(ex_);
356  throw ConnectionException(ex_.what());
357  }
358  catch(...)
359  {
360  throw ConnectionException("Unknown exception thrown by "
361  "the HAClient's delay strategy.");
362  }
363  }
364 
365  Mutex _connectLock;
366  Mutex _connectAndLogonLock;
367  int _timeout;
368  unsigned int _reconnectDelay;
369  ReconnectDelayStrategy _reconnectDelayStrategy;
370  ServerChooser _serverChooser;
371  volatile bool _disconnected;
372  std::string _logonOptions;
373 
374 }; // class HAClientImpl
375 
376 }// namespace AMPS
377 
378 #endif //_HACLIENTIMPL_H_
379 
Provides AMPS::MemorySubscriptionManager, used by an AMPS::HAClient to resubmit subscriptions if conn...
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:136