MIRA
RPCClient.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012 by
3  * MetraLabs GmbH (MLAB), GERMANY
4  * and
5  * Neuroinformatics and Cognitive Robotics Labs (NICR) at TU Ilmenau, GERMANY
6  * All rights reserved.
7  *
8  * Contact: info@mira-project.org
9  *
10  * Commercial Usage:
11  * Licensees holding valid commercial licenses may use this file in
12  * accordance with the commercial license agreement provided with the
13  * software or, alternatively, in accordance with the terms contained in
14  * a written agreement between you and MLAB or NICR.
15  *
16  * GNU General Public License Usage:
17  * Alternatively, this file may be used under the terms of the GNU
18  * General Public License version 3.0 as published by the Free Software
19  * Foundation and appearing in the file LICENSE.GPL3 included in the
20  * packaging of this file. Please review the following information to
21  * ensure the GNU General Public License version 3.0 requirements will be
22  * met: http://www.gnu.org/copyleft/gpl.html.
23  * Alternatively you may (at your option) use any later version of the GNU
24  * General Public License if such license has been publicly approved by
25  * MLAB and NICR (or its successors, if any).
26  *
27  * IN NO EVENT SHALL "MLAB" OR "NICR" BE LIABLE TO ANY PARTY FOR DIRECT,
28  * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
29  * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF "MLAB" OR
30  * "NICR" HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * "MLAB" AND "NICR" SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
33  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
34  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
35  * ON AN "AS IS" BASIS, AND "MLAB" AND "NICR" HAVE NO OBLIGATION TO
36  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR MODIFICATIONS.
37  */
38 
47 #ifndef _MIRA_RPCCLIENT_H_
48 #define _MIRA_RPCCLIENT_H_
49 
50 #ifndef Q_MOC_RUN
51 #include <boost/preprocessor/repetition.hpp>
52 #include <boost/thread/mutex.hpp>
53 #endif
54 
56 
57 #include <rpc/AbstractRPCClient.h>
58 #include <rpc/RPCSignature.h>
59 #include <rpc/RPCFuture.h>
60 #include <rpc/RPCMacros.h>
61 
62 
63 namespace mira {
64 
66 
105 {
106 protected:
107 
113  virtual ~PendingResponseBase() {}
115  virtual void handleResponse(void* responsePtr) = 0;
117  virtual void handleServiceRemoved() = 0;
119  virtual int getBackendTypeId() const = 0;
120  };
121 
135  template <typename Backend, typename R>
137 
138  typedef typename Backend::ClientResponse Response;
139 
141 
147  virtual void handleResponse(void* responsePtr) {
148  Response* concreteResponsePtr = static_cast<Response*>(responsePtr);
149  concreteResponsePtr->getReturn(promise);
150  }
151 
155  virtual void handleServiceRemoved() {
156  promise.set_exception(boost::copy_exception(XRPC("Service removed (unpublished or remote disconnected)")));
157  }
158 
164  virtual int getBackendTypeId() const { return backendTypeId; }
165 
167  boost::promise<R> promise;
169  };
170 
171  typedef boost::shared_ptr<PendingResponseBase> PendingResponsePtr;
172 
173 private:
174  template<typename Backend, typename Head, typename... Tail>
175  void setParameters(typename Backend::ClientRequest& request, Head&& head, Tail&&... tail)
176  {
177  request.setParameter(std::forward<Head>(head));
178  setParameters<Backend>(request, std::forward<Tail>(tail)...);
179  }
180 
181  template<typename Backend>
182  void setParameters(typename Backend::ClientRequest& request)
183  {}
184 
185 public:
218  template<typename Backend, typename R, typename... ARGS>
219  RPCFuture<R> call(typename Backend::ClientRequest& request, const std::string& service,
220  const std::string& method, ARGS&&... args)
221  {
222  std::string callId = request.generateCallID();
223  RPCFuture<R> future = addPendingResponse<Backend, R>(callId);
224  request.setHeader(std::move(callId), service, makeRPCSignature<R, ARGS...>(method));
225  setParameters<Backend>(request, std::forward<ARGS>(args)...);
226  return future;
227  }
228 
241  template <typename Backend, typename R>
242  RPCFuture<R> addPendingResponse(const std::string& callId)
243  {
244  boost::shared_ptr<PendingResponse<Backend, R>> ptr(new PendingResponse<Backend,R>());
245  boost::mutex::scoped_lock lock(mMutex);
246  // the following may overwrite an existing pending response, which is
247  // okay, as it might be intended by the user.
248  mPendingResponses[callId] = ptr;
249  return RPCFuture<R>(ptr->promise.get_future(), this, callId);
250  }
251 
252 public:
253 
260  template <typename Backend>
261  std::string handleResponse(typename Backend::ClientResponse& response)
262  {
263  std::string callId;
264  response.getHeader(callId);
265 
266  boost::mutex::scoped_lock lock(mMutex);
267  auto it = mPendingResponses.find(callId);
268 
269  // if call was already removed since future was destroyed, we can skip
270  // handling the response and save performance
271  if(it==mPendingResponses.end())
272  return "";
273 
274  // check if expected response type and the given response type do match
275  if(typeId<Backend>()!=it->second->getBackendTypeId())
276  MIRA_THROW(XLogical, "The expected Response Backend type does not match "
277  "the given Response. Probably you sent an RPC call with a "
278  "different Request Backend that does not match to the received "
279  "Response");
280 
281  PendingResponsePtr r = it->second;
282  mPendingResponses.erase(it);
283  lock.unlock();
284 
285  r->handleResponse(&response);
286 
287  // the pending response r and its promise will be destroyed here,
288  // which is okay, since the value or exception was set in
289  // handleResponse above, hence no "broken promise" will occur
290 
291  return callId;
292  }
293 
294  void handleServiceRemoved(const std::string& callId)
295  {
296  boost::mutex::scoped_lock lock(mMutex);
297  auto it = mPendingResponses.find(callId);
298 
299  // if call was already removed since future was destroyed, we can skip this
300  if(it==mPendingResponses.end())
301  return;
302 
303  PendingResponsePtr r = it->second;
304  mPendingResponses.erase(it);
305  lock.unlock();
306 
307  r->handleServiceRemoved();
308  }
309 
315  virtual void onDestructFuture(const std::string& callId)
316  {
317  boost::mutex::scoped_lock lock(mMutex);
318  mPendingResponses.erase(callId);
319  }
320 
321 private:
322 
323  typedef std::map<std::string, PendingResponsePtr> PendingResponses;
324  PendingResponses mPendingResponses;
325  boost::mutex mMutex;
326 };
327 
328 
330 
331 }
332 
333 
334 #endif
TypeId typeId()
Generates unique IDs for different types.
Definition: TypeId.h:94
The RPCClient is responsible for handling the client-side of an rpc call.
Definition: RPCClient.h:104
void handleServiceRemoved(const std::string &callId)
Definition: RPCClient.h:294
specialize cv::DataType for our ImgPixel and inherit from cv::DataType<Vec>
Definition: IOService.h:67
PendingResponse template class.
Definition: RPCClient.h:136
virtual int getBackendTypeId() const =0
Implementation of RPCFuture.
An RPCFuture is a proxy for the result of an asynchronous RPC call.
Definition: RPCFuture.h:173
#define MIRA_THROW(ex, msg)
Macro for throwing an exception.
Definition: Exception.h:81
RPCFuture< R > addPendingResponse(const std::string &callId)
Adds a new "PendingResponse" object for a call (with the specified callid) that was either generated ...
Definition: RPCClient.h:242
Contains the base interface of all Reflectors, Serializers, etc.
Abstract interface for RPCClient.
Abstract interface for RPCClient.
Definition: AbstractRPCClient.h:60
An exception that is thrown by the RPCServer if an RPC call fails.
Definition: RPCError.h:76
RPCSignature for storing all information about an RPC method signature.
int backendTypeId
Definition: RPCClient.h:168
virtual ~PendingResponseBase()
Definition: RPCClient.h:113
Interface for PendingResponse.
Definition: RPCClient.h:112
boost::shared_ptr< PendingResponseBase > PendingResponsePtr
Definition: RPCClient.h:171
virtual void onDestructFuture(const std::string &callId)
Is called by the RPCFutures that are created by the call() method upon the destruction of the RPCFutu...
Definition: RPCClient.h:315
Utility macros used in generating RPC method definitions with BOOST_PP.
virtual void handleResponse(void *responsePtr)
Handles the response by calling getReturn() of the RPCResponse backend.
Definition: RPCClient.h:147
virtual void handleServiceRemoved()
Handles the service being removed before a response is received.
Definition: RPCClient.h:155
RPCFuture< R > call(typename Backend::ClientRequest &request, const std::string &service, const std::string &method, ARGS &&... args)
Generates an RPCRequest for an RPC call.
Definition: RPCClient.h:219
PendingResponse()
Definition: RPCClient.h:140
Backend::ClientResponse Response
Definition: RPCClient.h:138
virtual void handleResponse(void *responsePtr)=0
virtual int getBackendTypeId() const
Returns the (non portable) type id of the used backend.
Definition: RPCClient.h:164
boost::promise< R > promise
The boost::promise object that is used for generating and setting the RPCFuture.
Definition: RPCClient.h:167
std::string handleResponse(typename Backend::ClientResponse &response)
Is called after the RPCServer has sent the response of the RPC call to handle the response and to set...
Definition: RPCClient.h:261