OSVR Framework (Internal Development Docs)  0.6-1962-g59773924
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
ConfigureServer.cpp
Go to the documentation of this file.
1 
11 // Copyright 2014 Sensics, Inc.
12 //
13 // Licensed under the Apache License, Version 2.0 (the "License");
14 // you may not use this file except in compliance with the License.
15 // You may obtain a copy of the License at
16 //
17 // http://www.apache.org/licenses/LICENSE-2.0
18 //
19 // Unless required by applicable law or agreed to in writing, software
20 // distributed under the License is distributed on an "AS IS" BASIS,
21 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 // See the License for the specific language governing permissions and
23 // limitations under the License.
24 
25 // Internal Includes
27 #include <osvr/Server/Server.h>
30 #include <osvr/Util/Verbosity.h>
31 #include "JSONResolvePossibleRef.h"
32 
33 // Library/third-party includes
34 #include <json/value.h>
35 #include <json/reader.h>
36 #include <boost/optional.hpp>
37 #include <boost/lexical_cast.hpp>
38 #include <boost/filesystem.hpp> // for stem()
39 #include <boost/algorithm/string/predicate.hpp> // for iends_with()
40 
41 // Standard includes
42 #include <stdexcept>
43 #include <iostream>
44 #include <vector>
45 
46 #undef OSVR_JSON_RESOLUTION_VERBOSE
47 
48 namespace osvr {
49 namespace server {
50 
51  inline void
52  printJsonReferenceResolutionAttempts(ResolveRefResult const &refReturn) {
53  for (auto &attempt : refReturn.fileAttempts) {
54  OSVR_DEV_VERBOSE("Tried loading "
55  << attempt.path << ": "
56  << fileLoadStatusToString(attempt.status)
57  << attempt.details);
58  }
59  }
60 
61  inline void
62  reportErrorInJsonReferenceParsing(const char *description,
63  Json::Value const &input,
64  ResolveRefResult const &refReturn) {
65  OSVR_DEV_VERBOSE("ERROR: Could not load "
66  << description
67  << " specified by: " << input.toStyledString());
68  printJsonReferenceResolutionAttempts(refReturn);
69  }
70 
71  class ConfigureServerData : boost::noncopyable {
72  public:
73  template <typename T> inline void parse(T &json) {
74  Json::Reader reader;
75  bool parsingSuccessful = reader.parse(json, root);
76  if (!parsingSuccessful) {
77  throw std::runtime_error("Error in parsing JSON: " +
78  reader.getFormattedErrorMessages());
79  }
80  }
81 
82  Json::Value const &getMember(const char *memberName) const {
83  return root[memberName];
84  }
85 
86  Json::Value root;
87  };
88 
90 
93 
94  void ConfigureServer::loadConfig(std::string const &json) {
95  m_data->parse(json);
96  }
97 
98  void ConfigureServer::loadConfig(std::istream &json) {
99  m_data->parse(json);
100  }
101 
102  static const char SERVER_KEY[] = "server";
103  static const char INTERFACE_KEY[] = "interface";
104  static const char LOCAL_KEY[] = "local";
105  static const char PORT_KEY[] = "port"; // not the triwizard cup.
106  static const char SLEEP_KEY[] = "sleep";
107 
109  Json::Value const &root(m_data->root);
110  bool local = true;
111  std::string iface;
112  boost::optional<int> port;
113 #ifdef _WIN32
114  int sleepTime = 0; // microseconds - default to 0 on Windows
115 #else
116  int sleepTime = 1000; // microseconds
117 #endif
118 
120  if (root.isMember(SERVER_KEY)) {
122  Json::Value jsonServer = root[SERVER_KEY];
123  Json::Value jsonInterface = jsonServer[INTERFACE_KEY];
124  Json::Value jsonLocal = jsonServer[LOCAL_KEY];
125  if (jsonInterface.isString()) {
126  local = false;
127  iface = jsonInterface.asString();
128  } else if (jsonLocal.isBool()) {
129  local = jsonLocal.asBool();
130  }
131 
132  Json::Value jsonPort = jsonServer[PORT_KEY];
133  if (jsonPort.isInt()) {
134  int myPort = jsonPort.asInt();
135  if (myPort < 1) {
136  throw std::out_of_range("Invalid port value: must be >= 1 "
137  "and for a non-admin execution, "
138  ">1024");
139  }
140  port = myPort;
141  }
142 
143  Json::Value jsonSleepTime = jsonServer[SLEEP_KEY];
144  if (jsonSleepTime.isDouble()) {
145  // Sleep time is in milliseconds in the config file.
146  // Convert to microseconds for internal use.
147  sleepTime = static_cast<int>(jsonSleepTime.asDouble() * 1000.0);
148  }
149  }
150 
153  if (local && !port) {
154  m_server = Server::createLocal();
155  } else {
158  boost::optional<std::string> host;
159  if (!iface.empty()) {
160  host = iface;
161  }
162  m_server = Server::create(connPtr, host, port);
163  }
164 
165  if (sleepTime > 0.0) {
166  m_server->setSleepTime(sleepTime);
167  }
168 
169  m_server->setHardwareDetectOnConnection();
170 
171  return m_server;
172  }
173 
174  static const char PLUGINS_KEY[] = "plugins";
176  Json::Value const &root(m_data->root);
177  const Json::Value plugins = root[PLUGINS_KEY];
178  bool success = true;
179  for (Json::ArrayIndex i = 0, e = plugins.size(); i < e; ++i) {
180  if (!plugins[i].isString()) {
181  success = false;
182  m_failedPlugins.push_back(std::make_pair(
183  "Plugin entry " + boost::lexical_cast<std::string>(i),
184  "Plugin name not string: " + plugins[i].toStyledString()));
185  // skip it!
186  continue;
187  }
188 
189  const std::string plugin = plugins[i].asString();
190  try {
191  m_server->loadPlugin(plugin);
192  m_successfulPlugins.push_back(plugin);
193  } catch (std::exception &e) {
194  m_failedPlugins.push_back(std::make_pair(plugin, e.what()));
195  success = false;
196  }
197  }
198  return success;
199  }
200 
202  ConfigureServer::getSuccessfulPlugins() const {
203  return m_successfulPlugins;
204  }
205 
208  return m_failedPlugins;
209  }
210 
211  static const char DRIVERS_KEY[] = "drivers";
212  static const char DRIVER_KEY[] = "driver";
213  static const char PLUGIN_KEY[] = "plugin";
214  static const char PARAMS_KEY[] = "params";
216  bool success = true;
217  Json::Value const &root(m_data->root);
218  Json::Value const &drivers = root[DRIVERS_KEY];
219  for (auto const &thisDriver : drivers) {
220  const bool hasPlugin = thisDriver[PLUGIN_KEY].isString();
221  const bool hasDriver = thisDriver[DRIVER_KEY].isString();
222  if (!hasPlugin && !hasDriver) {
223  success = false;
224  m_failedInstances.push_back(std::make_pair(
225  "?", "Entry present in drivers but lacking both a driver "
226  "name and a plugin name"));
227  // Skip this one.
228  continue;
229  }
230  if (!hasPlugin) {
231  success = false;
232  m_failedInstances.push_back(
233  std::make_pair("?/" + thisDriver[DRIVER_KEY].asString(),
234  "Entry present in drivers with a driver "
235  "name but lacking a plugin name"));
236  // Skip this one.
237  continue;
238  }
239 
240  const std::string plugin = thisDriver[PLUGIN_KEY].asString();
241 
242  if (!hasDriver) {
243  success = false;
244  m_failedInstances.push_back(std::make_pair(
245  plugin + "/?", "Entry present in drivers with a plugin "
246  "name but lacking a driver name"));
247  // Skip this one.
248  continue;
249  }
250 
251  const std::string driver = thisDriver[DRIVER_KEY].asString();
252 
253  try {
254  m_server->instantiateDriver(
255  plugin, driver, thisDriver[PARAMS_KEY].toStyledString());
256 
257  m_successfulInstances.push_back(plugin + "/" + driver);
258  } catch (std::exception &e) {
259  m_failedInstances.push_back(
260  std::make_pair(plugin + "/" + driver, e.what()));
261  success = false;
262  }
263  }
264  return success;
265  }
266 
268  ConfigureServer::getSuccessfulInstantiations() const {
269  return m_successfulInstances;
270  }
271 
274  return m_failedInstances;
275  }
276 
277  static const char ROUTES_KEY[] = "routes";
278  bool ConfigureServer::processRoutes() {
279  bool success = false;
280  Json::Value const &routes = m_data->getMember(ROUTES_KEY);
281  if (routes.isNull()) {
282  return success;
283  }
284  for (Json::ArrayIndex i = 0, e = routes.size(); i < e; ++i) {
285  const Json::Value thisRoute = routes[i];
286  m_server->addRoute(thisRoute.toStyledString());
287  success = true;
288  }
289  return success;
290  }
291 
292  static const char ALIASES_KEY[] = "aliases";
293  bool ConfigureServer::processAliases() {
294  bool success = false;
295  Json::Value const &aliases = m_data->getMember(ALIASES_KEY);
296  if (aliases.isNull()) {
297  return success;
298  }
299  success = m_server->addAliases(aliases);
300 
301  return success;
302  }
303  static const char EXTERNALDEVICES_KEY[] = "externalDevices";
304  static const char DEVICENAME_KEY[] = "deviceName";
305  static const char DESCRIPTOR_KEY[] = "descriptor";
307  bool success = false;
308  Json::Value const &devices = m_data->getMember(EXTERNALDEVICES_KEY);
309  if (devices.isNull()) {
310  return success;
311  }
312  for (auto const &devPath : devices.getMemberNames()) {
313  auto const &dev = devices[devPath];
314  auto const &devName = dev[DEVICENAME_KEY];
315  auto const &server = dev[SERVER_KEY];
316  auto descriptor = resolvePossibleRef(dev[DESCRIPTOR_KEY]);
317  if (devName.isNull()) {
318  OSVR_DEV_VERBOSE(
319  "Missing 'deviceName' for external device entry of "
320  << devPath << " : " << dev.toStyledString());
321  continue;
322  }
323  if (server.isNull()) {
324  OSVR_DEV_VERBOSE(
325  "Missing 'server' for external device entry of "
326  << devPath << " : " << dev.toStyledString());
327  continue;
328  }
329 
330  if (descriptor.isNull()) {
331  OSVR_DEV_VERBOSE("Missing 'descriptor' file or object for "
332  "external device entry of "
333  << devPath << " : " << dev.toStyledString());
334  continue;
335  }
336 
337  m_server->addExternalDevice(devPath, devName.asString(),
338  server.asString(),
339  descriptor.toStyledString());
340  success = true;
341  }
342  return success;
343  }
344 
345  static const char DISPLAY_KEY[] = "display";
346  static const char DISPLAY_PATH[] = "/display";
348  bool success = false;
349  Json::Value const &display = m_data->getMember(DISPLAY_KEY);
350  if (display.isNull()) {
351  return success;
352  }
353 
354  auto refReturn = resolvePossibleRefWithDetails(display);
355 
356  if (refReturn.result.isNull()) {
357  reportErrorInJsonReferenceParsing(
358  "an object or display descriptor file", display, refReturn);
359  return success;
360  }
361 
362 #ifdef OSVR_JSON_RESOLUTION_VERBOSE
363  printJsonReferenceResolutionAttempts(refReturn);
364 #endif
365 
366  // OK, got it.
368  success = m_server->addString(DISPLAY_PATH,
369  refReturn.result.toStyledString());
370  return success;
371  }
372 
373  static const char RENDERMANAGER_KEY[] = "renderManagerConfig";
374  static const char RENDERMANAGER_PATH[] = "/renderManagerConfig";
376  bool success = false;
377  Json::Value const &renderManager = m_data->getMember(RENDERMANAGER_KEY);
378  if (renderManager.isNull()) {
379  return success;
380  }
381 
382  auto refReturn = resolvePossibleRefWithDetails(renderManager);
383 
384  if (refReturn.result.isNull()) {
385  reportErrorInJsonReferenceParsing(
386  "an object or RenderManager config file", renderManager,
387  refReturn);
388  return success;
389  }
390 
391 #ifdef OSVR_JSON_RESOLUTION_VERBOSE
392  printJsonReferenceResolutionAttempts(refReturn);
393 #endif
394 
395  // OK, got it
397  success = m_server->addString(RENDERMANAGER_PATH,
398  refReturn.result.toStyledString());
399  return success;
400  }
401 
402  void ConfigureServer::loadAutoPlugins() { m_server->loadAutoPlugins(); }
403 
404 } // namespace server
405 } // namespace osvr
OSVR_SERVER_EXPORT ~ConfigureServer()
Destructor.
OSVR_SERVER_EXPORT ErrorList const & getFailedInstantiations() const
Get a reference to the list of drivers instantiateDrivers() tried to instantiate but failed...
Header.
OSVR_SERVER_EXPORT void loadConfig(std::string const &json)
Loads and parses the provided json.
OSVR_SERVER_EXPORT bool processDisplay()
Process a display element in the config.
OSVR_SERVER_EXPORT bool processRenderManagerParameters()
Process a RenderManager config element in the config.
static ConnectionPtr createSharedConnection(boost::optional< std::string const & > iface, boost::optional< int > port)
Factory method to create a shared connection.
Definition: Connection.cpp:53
std::vector< std::string > SuccessList
Container for plugin/driver names.
OSVR_SERVER_EXPORT ErrorList const & getFailedPlugins() const
Get a reference to the list of plugins loadPlugins() tried but failed to load, along with any excepti...
shared_ptr< Connection > ConnectionPtr
How one must hold a Connection.
Definition: ConnectionPtr.h:40
OSVR_SERVER_EXPORT ServerPtr constructServer()
Creates a server, choosing the factory method according to the passed JSON configuration.
Header declaring osvr::server::Server.
static OSVR_SERVER_EXPORT ServerPtr create(connection::ConnectionPtr const &conn)
Create a server object with a provided connection.
Definition: Server.cpp:49
OSVR_SERVER_EXPORT void loadAutoPlugins()
Loads all plugins not marked for manual load.
OSVR_SERVER_EXPORT bool instantiateDrivers()
Configures and instantiates the drivers as specified.
std::vector< ErrorPair > ErrorList
Container for plugin/driver names and error messages.
Json::Value resolvePossibleRef(Json::Value const &input, bool stringAcceptableResult, std::vector< std::string > const &searchPath)
Given an input that might be a filename, might be a JSON Pointer-style $ref object, and might just directly be an object, return the object desired.
OSVR_SERVER_EXPORT ConfigureServer()
Constructor.
ResolveRefResult resolvePossibleRefWithDetails(Json::Value const &input, bool stringAcceptableResult, std::vector< std::string > const &searchPath)
Header.
OSVR_SERVER_EXPORT bool processExternalDevices()
Process any external devices in the config.
shared_ptr< Server > ServerPtr
How one should hold a Server.
Definition: ServerPtr.h:39
Internal, configured header file for verbosity macros.
OSVR_SERVER_EXPORT bool loadPlugins()
Loads the plugins contained in an array with key plugins in the configuration.
static OSVR_SERVER_EXPORT ServerPtr createLocal()
Create a server object with a local-only (but still IP-based) connection.
Definition: Server.cpp:40