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 
61 
62 namespace mira {
63 
65 
104 {
105 protected:
106 
112  virtual ~PendingResponseBase() {}
114  virtual void handleResponse(void* responsePtr) = 0;
116  virtual void handleServiceRemoved() = 0;
118  virtual int getBackendTypeId() const = 0;
119  };
120 
134  template <typename Backend, typename R>
136 
137  typedef typename Backend::ClientResponse Response;
138 
140 
146  virtual void handleResponse(void* responsePtr) {
147  Response* concreteResponsePtr = static_cast<Response*>(responsePtr);
148  concreteResponsePtr->getReturn(promise);
149  }
150 
154  virtual void handleServiceRemoved() {
155  promise.set_exception(boost::copy_exception(XRPC("Service removed (unpublished or remote disconnected)")));
156  }
157 
163  virtual int getBackendTypeId() const { return backendTypeId; }
164 
166  boost::promise<R> promise;
168  };
169 
170  typedef boost::shared_ptr<PendingResponseBase> PendingResponsePtr;
171 
172 private:
173  template<typename Backend, typename Head, typename... Tail>
174  void setParameters(typename Backend::ClientRequest& request, Head&& head, Tail&&... tail)
175  {
176  request.setParameter(std::forward<Head>(head));
177  setParameters<Backend>(request, std::forward<Tail>(tail)...);
178  }
179 
180  template<typename Backend>
181  void setParameters(typename Backend::ClientRequest& request)
182  {}
183 
184 public:
217  template<typename Backend, typename R, typename... ARGS>
218  RPCFuture<R> call(typename Backend::ClientRequest& request, const std::string& service,
219  const std::string& method, ARGS&&... args)
220  {
221  std::string callId = request.generateCallID();
222  RPCFuture<R> future = addPendingResponse<Backend, R>(callId);
223  request.setHeader(std::move(callId), service, makeRPCSignature<R, ARGS...>(method));
224  setParameters<Backend>(request, std::forward<ARGS>(args)...);
225  return future;
226  }
227 
240  template <typename Backend, typename R>
241  RPCFuture<R> addPendingResponse(const std::string& callId)
242  {
243  boost::shared_ptr<PendingResponse<Backend, R>> ptr(new PendingResponse<Backend,R>());
244  boost::mutex::scoped_lock lock(mMutex);
245  // the following may overwrite an existing pending response, which is
246  // okay, as it might be intended by the user.
247  mPendingResponses[callId] = ptr;
248  return RPCFuture<R>(ptr->promise.get_future(), this, callId);
249  }
250 
251 public:
252 
259  template <typename Backend>
260  std::string handleResponse(typename Backend::ClientResponse& response)
261  {
262  std::string callId;
263  response.getHeader(callId);
264 
265  boost::mutex::scoped_lock lock(mMutex);
266  auto it = mPendingResponses.find(callId);
267 
268  // if call was already removed since future was destroyed, we can skip
269  // handling the response and save performance
270  if(it==mPendingResponses.end())
271  return "";
272 
273  // check if expected response type and the given response type do match
274  if(typeId<Backend>()!=it->second->getBackendTypeId())
275  MIRA_THROW(XLogical, "The expected Response Backend type does not match "
276  "the given Response. Probably you sent an RPC call with a "
277  "different Request Backend that does not match to the received "
278  "Response");
279 
280  PendingResponsePtr r = it->second;
281  mPendingResponses.erase(it);
282  lock.unlock();
283 
284  r->handleResponse(&response);
285 
286  // the pending response r and its promise will be destroyed here,
287  // which is okay, since the value or exception was set in
288  // handleResponse above, hence no "broken promise" will occur
289 
290  return callId;
291  }
292 
293  void handleServiceRemoved(const std::string& callId)
294  {
295  boost::mutex::scoped_lock lock(mMutex);
296  auto it = mPendingResponses.find(callId);
297 
298  // if call was already removed since future was destroyed, we can skip this
299  if(it==mPendingResponses.end())
300  return;
301 
302  PendingResponsePtr r = it->second;
303  mPendingResponses.erase(it);
304  lock.unlock();
305 
306  r->handleServiceRemoved();
307  }
308 
314  virtual void onDestructFuture(const std::string& callId)
315  {
316  boost::mutex::scoped_lock lock(mMutex);
317  mPendingResponses.erase(callId);
318  }
319 
320 private:
321 
322  typedef std::map<std::string, PendingResponsePtr> PendingResponses;
323  PendingResponses mPendingResponses;
324  boost::mutex mMutex;
325 };
326 
327 
329 
330 }
331 
332 
333 #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:103
void handleServiceRemoved(const std::string &callId)
Definition: RPCClient.h:293
specialize cv::DataType for our ImgPixel and inherit from cv::DataType<Vec>
Definition: IOService.h:67
PendingResponse template class.
Definition: RPCClient.h:135
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:82
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:241
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:167
virtual ~PendingResponseBase()
Definition: RPCClient.h:112
Interface for PendingResponse.
Definition: RPCClient.h:111
boost::shared_ptr< PendingResponseBase > PendingResponsePtr
Definition: RPCClient.h:170
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:314
virtual void handleResponse(void *responsePtr)
Handles the response by calling getReturn() of the RPCResponse backend.
Definition: RPCClient.h:146
virtual void handleServiceRemoved()
Handles the service being removed before a response is received.
Definition: RPCClient.h:154
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:218
PendingResponse()
Definition: RPCClient.h:139
Backend::ClientResponse Response
Definition: RPCClient.h:137
virtual void handleResponse(void *responsePtr)=0
virtual int getBackendTypeId() const
Returns the (non portable) type id of the used backend.
Definition: RPCClient.h:163
boost::promise< R > promise
The boost::promise object that is used for generating and setting the RPCFuture.
Definition: RPCClient.h:166
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:260