RSB  0.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Factory.cpp
Go to the documentation of this file.
1 /* ============================================================
2  *
3  * This file is a part of the RSB project.
4  *
5  * Copyright (C) 2011 by Johannes Wienke <jwienke at techfak dot uni-bielefeld dot de>
6  * Copyright (C) 2012, 2013 Jan Moringen <jmoringe@techfak.uni-bielefeld.de>
7  *
8  * This file may be licensed under the terms of the
9  * GNU Lesser General Public License Version 3 (the ``LGPL''),
10  * or (at your option) any later version.
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the LGPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the LGPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/lgpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  * The development of this software was supported by:
23  * CoR-Lab, Research Institute for Cognition and Robotics
24  * Bielefeld University
25  *
26  * ============================================================ */
27 
28 #include "Factory.h"
29 
30 #include <boost/filesystem/fstream.hpp>
31 
32 #include <rsc/config/Configuration.h>
33 #include <rsc/config/Environment.h>
34 
35 #include <rsc/logging/OptionBasedConfigurator.h>
36 
37 #include <rsc/plugins/Manager.h>
38 #include <rsc/plugins/Configurator.h>
39 
40 #include <rsb/Version.h>
41 
43 
44 #include "converter/converters.h"
45 #include "converter/Repository.h"
47 
48 #include "transport/transports.h"
49 
50 #include "LocalService.h"
51 
52 using namespace std;
53 
54 using namespace rsc::config;
55 using namespace rsc::logging;
56 using namespace rsc::runtime;
57 
58 using namespace rsb::converter;
59 using namespace rsb::transport;
60 
61 namespace { // anonymous namespace for file-local helper functions.
62 
64  SERIALIZATION,
65  DESERIALIZATION
66 };
67 
68 template<typename C>
69 std::map<typename C::value_type::first_type,
70  typename C::value_type::second_type>
71 converterSelectionToMap(const C& container, ConverterDirection direction) {
72  typedef typename C::value_type::first_type first_type;
73  typedef typename C::value_type::second_type second_type;
74 
75  typedef typename C::const_iterator const_iterator;
76 
77  std::map<first_type, second_type> result;
78  for (const_iterator it = container.begin(); it != container.end(); ++it) {
79  switch (direction) {
80  case DESERIALIZATION:
81  result[it->first] = it->second;
82  break;
83  case SERIALIZATION:
84  if (result.find(it->second) != result.end()) {
85  throw std::invalid_argument(
86  boost::str(
87  boost::format(
88  "Multiple wire-schemas (%1%, %2%) selected for data-type %3%.\n"
89  "Probably you wrote the lines transport.<name>.cpp.%1% = %3% "
90  "and transport.<name>.cpp.%2% = %3% in you rsb config. One of "
91  "these should be deleted.")
92  % it->first
93  % result[it->second]
94  % it->second
95  )
96  );
97  }
98  result[it->second] = it->first;
99  break;
100  default:
101  assert(false);
102  throw std::runtime_error("got unexpected serialization direction");
103  }
104  }
105  return result;
106 }
107 
108 Properties
109 prepareConnectorOptions(const rsb::ParticipantConfig::Transport& config,
110  ConverterDirection direction,
111  rsc::logging::LoggerPtr logger) {
112  Properties options = config.getOptions();
113  RSCDEBUG(logger, "Supplied connector options " << options);
114 
115  // For local transport, we do not mess with the converter
116  // configuration since it is not used (or even touched) anyway.
117  //
118  // For remote transports, we build a converter selection strategy
119  // - suitable for direction - and put it into the "converters"
120  // property of the transport configuration.
121  //
122  // Note that config.getConverters() only returns /converter
123  // disambiguations/. These are used to guide the actual converter
124  // selection in converterSelectionToMap().
125  if (rsb::transport::isRemote(config.getName()) && !options.has("converters")) {
126  RSCDEBUG(logger, "Converter configuration for transport `"
127  << config.getName() << "': " << config.getConverters());
128 
129  ConverterSelectionStrategy<string>::Ptr converters; // TODO we should not have to know the transport's wire-type here
130  switch (direction) {
131  case SERIALIZATION:
132  converters
133  = converterRepository<string>() // TODO wire-type
134  ->getConvertersForSerialization
135  (converterSelectionToMap(config.getConverters(), direction));
136  break;
137  case DESERIALIZATION:
138  converters
139  = converterRepository<string>() // TODO wire-type
140  ->getConvertersForDeserialization
141  (converterSelectionToMap(config.getConverters(), direction));
142  break;
143  default:
144  assert(false);
145  throw std::runtime_error("got unexpected serialization direction");
146  }
147  RSCDEBUG(logger, "Selected converters for transport `"
148  << config.getName() << "': " << converters);
149  options["converters"] = converters;
150  }
151 
152  return options;
153 }
154 
155 }
156 
157 namespace rsb {
158 
159 Factory& Factory::getInstance() {
160  return getFactory();
161 }
162 
164  return Factory::getInstanceBase();
165 }
166 
167 Factory& Factory::getInstanceBase() {
168  return rsc::patterns::Singleton<Factory>::getInstance();
169 }
170 
171 Factory::Factory() :
172  logger(Logger::getLogger("rsb.Factory")),
173  pluginManager(new rsc::plugins::Manager()) {
174 
175  // Configure RSC-based logging.
176  {
177  rsc::logging::OptionBasedConfigurator configurator;
178  configure(configurator, "rsb.conf", "RSC_", 0, 0, false, Version::installPrefix());
179  }
180 
181  // Register default implementation for all extension points.
182  RSCINFO(this->logger, "Registering default implementations");
186 
187  // Configure plugin path and load plugins to register additional
188  // implementations for extension points.
189  //
190  // We use the following default plugin path:
191  // 1. $HOME/.rsb$MAJOR.$MINOR/plugins
192  // 2. $libdir/rsb$MAJOR.$MINOR/plugins
193  RSCINFO(this->logger, "Processing plugin configuration");
194  {
195  string versioned = str(boost::format("rsb%1%.%2%")
196  % RSB_VERSION_MAJOR
197  % RSB_VERSION_MINOR);
198  vector<boost::filesystem::path> defaultPath;
199  // It may be impossible to determine a home directory for the
200  // current user. Warn, but don't throw.
201  try {
202  defaultPath.push_back(userHomeDirectory() / ("." + versioned) / "plugins");
203  } catch (const runtime_error& e) {
204  RSCWARN(this->logger,
205  "Failed to determine user-specific plugin directory: "
206  << e.what());
207  }
208  defaultPath.push_back(Version::libdir() / versioned / "plugins");
209  rsc::plugins::Configurator configurator(pluginManager, defaultPath);
210  configure(configurator, "rsb.conf", "RSB_", 0, 0, true, Version::installPrefix());
211  }
212 
213  // Setup default participant config
214  //
215  // Collect all available connector implementations:
216  // + In-push
217  // + In-pull
218  // + Out
219  // Disable discovered connectors with the exception of the
220  // socket transport.
221  set<string> availableTransports = getAvailableTransports(IN_PUSH | IN_PULL | OUT);
222 
224  for (set<string>::const_iterator it = availableTransports.begin();
225  it != availableTransports.end(); ++it) {
226  this->defaultConfig.addTransport(ParticipantConfig::Transport(*it, *it == "socket"));
227  }
228 
229  // If there is only one transport, we can blindly enable it since
230  // the user could end up without any enabled transport otherwise.
231  if (this->defaultConfig.getTransports().size() == 1) {
232  string name = this->defaultConfig.getTransports().begin()->getName();
233  this->defaultConfig.mutableTransport(name).setEnabled(true);
234  }
235 
236  // Merge with user configuration (configuration files, environment
237  // variables)
238  configure(this->defaultConfig, "rsb.conf", "RSB_", 0, 0, true, Version::installPrefix());
239 
240  // Issue a warning if the combination of available transport
241  // implementations and user configuration leads to no enabled
242  // transports.
243  if (this->defaultConfig.getTransports().empty()) {
244  RSCWARN(logger, "No transports are enabled. This is probably a configuration error or an internal RSB error.");
245  }
246 
247  RSCDEBUG(logger, "Default config " << defaultConfig);
248 }
249 
251 }
252 
254  const string& dataType,
255  const ParticipantConfig& config) {
256  return InformerBasePtr(new InformerBase(createOutConnectors(config), scope, config, dataType));
257 }
258 
259 
261  const ParticipantConfig& config) {
262  return ListenerPtr(new Listener(createInPushConnectors(config), scope, config));
263 }
264 
266  const ParticipantConfig& config) {
267  return ReaderPtr(new Reader(createInPullConnectors(config), scope, config));
268 }
269 
271  const ParticipantConfig &listenerConfig,
272  const ParticipantConfig &informerConfig) {
273  return patterns::ServerPtr(
274  new patterns::Server(scope, listenerConfig, informerConfig));
275 }
276 
278  const ParticipantConfig &listenerConfig,
279  const ParticipantConfig &informerConfig) {
281  new patterns::RemoteServer(scope, listenerConfig, informerConfig));
282 }
283 
285  boost::recursive_mutex::scoped_lock lock(configMutex);
286  return defaultConfig;
287 }
288 
290  boost::recursive_mutex::scoped_lock lock(configMutex);
291  this->defaultConfig = config;
292 }
293 
295  return ServicePtr(new LocalService(scope));
296 }
297 
298 vector<InPullConnectorPtr>
300  // Note: getTransports() only returns *enabled* transports.
301  vector<InPullConnectorPtr> connectors;
302  set<ParticipantConfig::Transport> configuredTransports = config.getTransports();
303  for (set<ParticipantConfig::Transport>::const_iterator transportIt =
304  configuredTransports.begin(); transportIt
305  != configuredTransports.end(); ++transportIt) {
306  RSCDEBUG(logger, "Trying to add connector " << *transportIt);
307  try {
308  connectors.push_back(InPullConnectorPtr(getInPullFactory()
309  .createInst(transportIt->getName(),
310  prepareConnectorOptions(*transportIt,
311  DESERIALIZATION,
312  this->logger))));
313  } catch (const exception& e) {
314  throw runtime_error(boost::str(boost::format("Error configuring connector `%1%', in-pull: %2%")
315  % transportIt->getName() % e.what()));
316  }
317  }
318  return connectors;
319 }
320 
321 vector<InPushConnectorPtr>
323  // Note: getTransports() only returns *enabled* transports.
324  vector<InPushConnectorPtr> connectors;
325  set<ParticipantConfig::Transport> configuredTransports = config.getTransports();
326  for (set<ParticipantConfig::Transport>::const_iterator transportIt =
327  configuredTransports.begin(); transportIt
328  != configuredTransports.end(); ++transportIt) {
329  RSCDEBUG(logger, "Trying to add connector " << *transportIt);
330  try {
331  connectors.push_back(InPushConnectorPtr(getInPushFactory()
332  .createInst(transportIt->getName(),
333  prepareConnectorOptions(*transportIt,
334  DESERIALIZATION,
335  this->logger))));
336  } catch (const exception& e) {
337  throw runtime_error(boost::str(boost::format("Error configuring connector `%1%', in-push: %2%")
338  % transportIt->getName() % e.what()));
339  }
340  }
341  return connectors;
342 }
343 
344 vector<OutConnectorPtr>
346  // Note: getTransports() only returns *enabled* transports.
347  vector<OutConnectorPtr> connectors;
348  set<ParticipantConfig::Transport> configuredTransports = config.getTransports();
349  for (set<ParticipantConfig::Transport>::const_iterator transportIt =
350  configuredTransports.begin(); transportIt
351  != configuredTransports.end(); ++transportIt) {
352  RSCDEBUG(logger, "Trying to add connector " << *transportIt);
353  try {
354  connectors.push_back(OutConnectorPtr(getOutFactory()
355  .createInst(transportIt->getName(),
356  prepareConnectorOptions(*transportIt,
357  SERIALIZATION,
358  this->logger))));
359  } catch (const exception& e) {
360  throw runtime_error(boost::str(boost::format("Error configuring connector `%1%', out: %2%")
361  % transportIt->getName() % e.what()));
362  }
363  }
364  return connectors;
365 }
366 
367 rsc::plugins::ManagerPtr Factory::getPluginManager() const {
368  return this->pluginManager;
369 }
370 
371 }