MongoDBStorageMixin.cpp
Go to the documentation of this file.
1 #include "MongoDBStorageMixin.h"
2 
3 #include <SimoxUtility/algorithm/string.h>
4 
8 
9 #include "util/filesystem.h"
10 
12 {
13  std::unique_ptr<mongocxx::instance> MongoDBStorageMixin::MongoCXXInstance = nullptr;
14  std::atomic_bool MongoDBStorageMixin::MongoCXXInitialized = false;
15  std::recursive_mutex MongoDBStorageMixin::MongoCXXConnectionPoolMutex;
16  std::map<std::string, std::unique_ptr<mongocxx::pool>>
17  MongoDBStorageMixin::MongoCXXConnectionPool = {};
18 
19  void
21  {
22  std::filesystem::path armarx_config_home = std::string(getenv("ARMARX_USER_CONFIG_DIR"));
23  if (!util::fs::directoryExists(armarx_config_home))
24  {
25  ARMARX_WARNING << ("Could not find an ArmarX Config folder. Tried: '" +
26  armarx_config_home.string() +
27  "' (from ARMARX_USER_CONFIG_DIR). If not set via scenario params "
28  "this means that LTM is disabled.");
29  return;
30  }
31 
32  std::ifstream cFile(armarx_config_home / "default.cfg");
33  if (cFile.is_open())
34  {
35  std::string line;
36  while (getline(cFile, line))
37  {
38  line.erase(std::remove_if(line.begin(), line.end(), isspace), line.end());
39  if (line.empty() || line[0] == '#')
40  {
41  continue;
42  }
43  auto delimiterPos = line.find("=");
44  const auto name = simox::alg::to_lower(line.substr(0, delimiterPos));
45  const auto value = line.substr(delimiterPos + 1);
46  if (host.empty() && name == "armarx.mongohost")
47  {
48  host = value;
49  }
50  if (port == 0 && name == "armarx.mongoport")
51  {
52  port = (unsigned int)std::stoi(value);
53  }
54  if (user.empty() && name == "armarx.mongouser")
55  {
56  user = value;
57  }
58  if (password.empty() && name == "armarx.mongopassword")
59  {
60  password = value;
61  }
62  }
63  }
64  }
65 
66  bool
68  {
69  // we always need a host and a port
70  return !host.empty() and port != 0;
71  }
72 
73  std::string
75  {
76  std::stringstream ss;
77  ss << "mongodb://";
78 
79  if (!user.empty())
80  {
81  ss << user;
82  if (!password.empty())
83  {
84  ss << ":" << password;
85  }
86  ss << "@";
87  }
88  ss << host;
89  return ss.str();
90  }
91 
92  std::string
94  {
95  // TODO: What happens if a connection exists and you would like to open another one with a different user (e.g. that sees different things)?
96  return "mongodb://" + host + ":" + std::to_string(port);
97  }
98 
99  std::string
101  {
102  return baseUri() + ":" + std::to_string(port) +
103  "/?minPoolSize=" + std::to_string(minPoolSize) +
104  "&maxPoolSize=" + std::to_string(maxPoolSize);
105  }
106 
107  std::string
109  {
110  return uri();
111  }
112 
114  const std::string& en,
115  const armem::MemoryID& id) :
116  settings(settings), exportName(en), _id(id)
117  {
118  }
119 
120  void
121  MongoDBStorageMixin::setHost(const std::string& n)
122  {
123  settings.host = n;
124  }
125 
126  void
127  MongoDBStorageMixin::setPort(const unsigned int n)
128  {
129  settings.port = n;
130  }
131 
132  void
133  MongoDBStorageMixin::setUser(const std::string& n)
134  {
135  settings.user = n;
136  }
137 
138  void
139  MongoDBStorageMixin::setPassword(const std::string& n)
140  {
141  settings.password = n;
142  }
143 
144  void
146  {
147  std::lock_guard l(MongoCXXConnectionPoolMutex);
148  if (!MongoCXXInitialized.exchange(true))
149  {
150  ARMARX_IMPORTANT << "INITIALIZE MONGODB";
151  MongoCXXInstance =
152  std::make_unique<mongocxx::instance>(); // This should be done only once.
153  }
154 
155  const auto key = settings.key();
156  const auto uri = settings.uri();
157  auto it = MongoCXXConnectionPool.find(key);
158  if (it == MongoCXXConnectionPool.end())
159  {
160  ARMARX_INFO << "Establishing new connection to: " << uri << " from id: " << _id.str();
161  mongocxx::uri u(uri);
162  auto pool = std::make_unique<mongocxx::pool>(u);
163  MongoCXXConnectionPool.emplace(settings.key(), std::move(pool));
164  }
165  }
166 
167  mongocxx::pool*
168  MongoDBStorageMixin::getPool(bool tryToConnect) const
169  {
170  std::lock_guard l(MongoCXXConnectionPoolMutex);
171  const auto key = settings.key();
172  auto it = MongoCXXConnectionPool.find(key);
173  if (it == MongoCXXConnectionPool.end())
174  {
175  if (tryToConnect)
176  {
177  // try to establish a connection and try again
178  connect();
179  return getPool(false);
180  }
181  return nullptr;
182  }
183  else
184  {
185  return it->second.get();
186  }
187  }
188 
189  mongocxx::pool*
190  MongoDBStorageMixin::ensurePool() const
191  {
192  std::lock_guard l(MongoCXXConnectionPoolMutex);
193  auto pool = getPool();
194  if (pool)
195  {
196  return pool;
197  }
198  else
199  {
200  throw armarx::LocalException("Pool could not be ensured...");
201  }
202  }
203 
204  void
206  {
207  // ensure a connection is made if not done already
208  connect();
209  }
210 
211  void
213  {
214  }
215 
216  bool
218  {
219  std::lock_guard l(MongoCXXConnectionPoolMutex);
220 
221  try
222  {
223  auto client = ensurePool()->acquire();
224  auto admin = client->database("admin");
225  auto result = admin.run_command(bsoncxx::builder::basic::make_document(
226  bsoncxx::builder::basic::kvp("isMaster", 1)));
227  return true;
228  }
229  catch (const std::exception& xcp)
230  {
231  return false;
232  }
233  return false; // should never happen
234  }
235 
236  std::string
238  {
239  return util::mongodb::toDocumentID(_id);
240  }
241 
242  std::string
244  {
246  }
247 
248  std::string
250  {
251  ARMARX_CHECK(!_id.memoryName.empty() and !_id.coreSegmentName.empty());
253  }
254 
255  std::string
257  {
258  return exportName;
259  }
260 
261  std::optional<mongocxx::database>
263  {
264  auto client = this->ensurePool()->acquire();
266  }
267 
268  std::optional<mongocxx::collection>
270  {
271  auto db = databaseExists();
272  if (!db)
273  {
274  return std::nullopt;
275  }
276 
278  }
279 
280  std::optional<mongocxx::collection>
282  {
283  auto db = databaseExists();
284  if (!db)
285  {
286  return std::nullopt;
287  }
288 
290  }
291 
292  std::optional<nlohmann::json>
294  {
296  }
297 
298  std::optional<nlohmann::json>
299  MongoDBStorageMixin::documentExists(const std::string& id) const
300  {
301  auto coll = collectionExists();
302  if (!coll)
303  {
304  return std::nullopt;
305  }
306 
307  nlohmann::json query;
308  query[ID] = getDocumentName();
309  return util::mongodb::documentExists(*coll, query);
310  }
311 
312  mongocxx::database
314  {
315  auto client = this->ensurePool()->acquire();
316  return util::mongodb::ensureDatabaseExists(*client, getDatabaseName(), createIfNotExistent);
317  }
318 
319  mongocxx::collection
321  {
322  auto db = ensureDatabaseExists(createIfNotExistent);
323  return util::mongodb::ensureCollectionExists(db, getCollectionName(), createIfNotExistent);
324  }
325 
326  mongocxx::collection
328  {
329  auto db = ensureDatabaseExists(createIfNotExistent);
331  db, getPreviousCollectionName(), createIfNotExistent);
332  }
333 
334  nlohmann::json
336  {
337  return ensureDocumentExists(getDocumentName(), createIfNotExistent);
338  }
339 
340  nlohmann::json
341  MongoDBStorageMixin::ensureDocumentExists(const std::string& id, bool createIfNotExistent)
342  {
343  auto coll = ensureCollectionExists(createIfNotExistent);
344 
345  nlohmann::json query;
346  query[ID] = getDocumentName();
347  return util::mongodb::ensureDocumentExists(coll, query, createIfNotExistent);
348  }
349 
350  void
351  MongoDBStorageMixin::writeDataToDocument(const std::string& id, const nlohmann::json& data)
352  {
353  auto coll = ensureCollectionExists(true);
354 
355  nlohmann::json query;
356  query[ID] = getDocumentName();
357 
358  nlohmann::json update;
359  update[DATA] = data;
361  }
362 
363  void
365  {
366  auto coll = ensurePreviousCollectionExists(true);
367 
368  nlohmann::json query;
369  query[ID] = _id.str();
370 
371  nlohmann::json update;
374  }
375 
376  void
378  {
379  auto coll = ensurePreviousCollectionExists(true);
380 
381  nlohmann::json query;
382  query[ID] = _id.str();
383 
384  nlohmann::json update;
386  update[TYPE] = type;
387 
389  }
390 
391  nlohmann::json
392  MongoDBStorageMixin::readDataFromDocument(const std::string& id) const
393  {
394  auto coll = collectionExists();
395  if (!coll)
396  {
397  // What to do here?
398  return {};
399  }
400 
401  if (!documentExists())
402  {
403  // What to do here?
404  return {};
405  }
406 
407  auto query =
408  nlohmann::json::parse(std::string("{\"") + ID + "\": " + getDocumentName() + "}");
409  return util::mongodb::readDataFromDocument(*coll, query);
410  }
411 
412  void
414  {
416  }
417 
418  nlohmann::json
420  {
422  }
423 
424  std::vector<nlohmann::json>
426  {
427  auto coll = collectionExists();
428  if (!coll)
429  {
430  return {};
431  }
432  return util::mongodb::getAllDocuments(*coll);
433  }
434 
437  {
438  return settings;
439  }
440 
441  void
443  {
444  ARMARX_CHECK_NOT_EMPTY(_id.memoryName) << " The full id was: " << _id.str();
445 
446  _id = n;
447  }
448 
449  void
451  {
452  exportName = n;
453  }
454 
455  void
456  MongoDBStorageMixin::configureMixin(const nlohmann::json& json)
457  {
458  }
459 } // namespace armarx::armem::server::ltm::detail::mixin
armarx::armem::server::ltm::util::mongodb::ensureDocumentExists
nlohmann::json ensureDocumentExists(mongocxx::collection &collection, const nlohmann::json &json, bool createIfNotExistent)
Definition: mongodb.cpp:181
armarx::armem::server::ltm::detail::mixin::MongoDBSettings::password
std::string password
Definition: MongoDBStorageMixin.h:21
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::stop
void stop()
Definition: MongoDBStorageMixin.cpp:212
armarx::armem::server::ltm::detail::mixin::MongoDBSettings::uri
std::string uri() const
Definition: MongoDBStorageMixin.cpp:100
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::collectionExists
std::optional< mongocxx::collection > collectionExists() const
Definition: MongoDBStorageMixin.cpp:269
armarx::armem::server::ltm::util::mongodb::toDocumentID
std::string toDocumentID(const armem::MemoryID &id)
Definition: mongodb.cpp:138
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::start
void start()
start
Definition: MongoDBStorageMixin.cpp:205
MongoDBStorageMixin.h
ARMARX_IMPORTANT
#define ARMARX_IMPORTANT
Definition: Logging.h:183
LocalException.h
armarx::armem::MemoryID::removeLeafItem
MemoryID removeLeafItem() const
Definition: MemoryID.cpp:329
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::configureMixin
void configureMixin(const nlohmann::json &json)
configuration
Definition: MongoDBStorageMixin.cpp:456
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::databaseExists
std::optional< mongocxx::database > databaseExists() const
Definition: MongoDBStorageMixin.cpp:262
armarx::armem::server::ltm::util::mongodb::databaseExists
std::optional< mongocxx::database > databaseExists(mongocxx::client &client, const std::string &databaseName)
Definition: mongodb.cpp:83
armarx::armem::server::ltm::detail::mixin::MongoDBSettings::maxPoolSize
int maxPoolSize
Definition: MongoDBStorageMixin.h:23
armarx::armem::server::ltm::util::mongodb::readDataFromDocument
nlohmann::json readDataFromDocument(mongocxx::collection &collection, const nlohmann::json &json)
Definition: mongodb.cpp:210
armarx::armem::MemoryID::str
std::string str(bool escapeDelimiters=true) const
Get a string representation of this memory ID.
Definition: MemoryID.cpp:102
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::getAllDocuments
std::vector< nlohmann::json > getAllDocuments() const
Definition: MongoDBStorageMixin.cpp:425
ARMARX_CHECK_NOT_EMPTY
#define ARMARX_CHECK_NOT_EMPTY(c)
Definition: ExpressionException.h:224
armarx::armem::server::ltm::detail::mixin::MongoDBSettings::host
std::string host
Definition: MongoDBStorageMixin.h:18
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::setPassword
void setPassword(const std::string &)
Definition: MongoDBStorageMixin.cpp:139
armarx::armem::server::ltm::detail::mixin::MongoDBSettings::minPoolSize
int minPoolSize
Definition: MongoDBStorageMixin.h:22
armarx::armem::MemoryID::coreSegmentName
std::string coreSegmentName
Definition: MemoryID.h:51
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::previousCollectionExists
std::optional< mongocxx::collection > previousCollectionExists() const
Definition: MongoDBStorageMixin.cpp:281
ARMARX_CHECK
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
Definition: ExpressionException.h:82
armarx::armem::server::ltm::detail::mixin::MongoDBSettings::isSet
bool isSet() const
Definition: MongoDBStorageMixin.cpp:67
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::setMixinMemoryID
void setMixinMemoryID(const armem::MemoryID &)
setter
Definition: MongoDBStorageMixin.cpp:442
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::ensurePreviousCollectionExists
mongocxx::collection ensurePreviousCollectionExists(bool createIfNotExistent=false)
Definition: MongoDBStorageMixin.cpp:327
armarx::armem::server::ltm::detail::mixin::MongoDBSettings::key
std::string key() const
Definition: MongoDBStorageMixin.cpp:93
cxxopts::value
std::shared_ptr< Value > value()
Definition: cxxopts.hpp:926
armarx::armem::MemoryID
A memory ID.
Definition: MemoryID.h:47
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::writeForeignKeyToPreviousDocument
void writeForeignKeyToPreviousDocument()
Definition: MongoDBStorageMixin.cpp:364
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::connect
void connect() const
Definition: MongoDBStorageMixin.cpp:145
armarx::armem::server::ltm::util::mongodb::getAllDocuments
std::vector< nlohmann::json > getAllDocuments(mongocxx::collection &collection)
Definition: mongodb.cpp:239
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::getSettings
MongoDBSettings getSettings() const
Definition: MongoDBStorageMixin.cpp:436
armarx::armem::server::ltm::detail::mixin::MongoDBSettings::port
unsigned int port
Definition: MongoDBStorageMixin.h:19
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::getCollectionName
std::string getCollectionName() const
Definition: MongoDBStorageMixin.cpp:243
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::setUser
void setUser(const std::string &)
Definition: MongoDBStorageMixin.cpp:133
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::setMixinExportName
void setMixinExportName(const std::string &n)
Definition: MongoDBStorageMixin.cpp:450
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::ensureCollectionExists
mongocxx::collection ensureCollectionExists(bool createIfNotExistent=false)
Definition: MongoDBStorageMixin.cpp:320
armarx::armem::server::ltm::util::mongodb::documentExists
std::optional< nlohmann::json > documentExists(mongocxx::collection &collection, const nlohmann::json &json)
Definition: mongodb.cpp:175
armarx::to_string
const std::string & to_string(const std::string &s)
Definition: StringHelpers.h:40
armarx::armem::server::ltm::util::mongodb::detail::update
bool update(mongocxx::collection &coll, const nlohmann::json &query, const nlohmann::json &update)
Definition: mongodb.cpp:67
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::ensureDocumentExists
nlohmann::json ensureDocumentExists(bool createIfNotExistent=false)
Definition: MongoDBStorageMixin.cpp:335
armarx::armem::server::ltm::util::mongodb::writeDataToDocument
void writeDataToDocument(mongocxx::collection &collection, const nlohmann::json &query, const nlohmann::json &update)
Definition: mongodb.cpp:221
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::FOREIGN_KEY
static const constexpr char * FOREIGN_KEY
Definition: MongoDBStorageMixin.h:109
armarx::armem::MemoryID::memoryName
std::string memoryName
Definition: MemoryID.h:50
armarx::armem::server::ltm::detail::mixin::MongoDBSettings::baseUri
std::string baseUri() const
Definition: MongoDBStorageMixin.cpp:74
armarx::armem::server::ltm::detail::mixin::MongoDBSettings::initializeFromArmarXConfig
void initializeFromArmarXConfig()
Fills missing fields from armarx config file.
Definition: MongoDBStorageMixin.cpp:20
ExpressionException.h
armarx::armem::server::ltm::util::mongodb::ensureCollectionExists
mongocxx::collection ensureCollectionExists(mongocxx::database &db, const std::string &collectionName, bool createIfNotExistent)
Definition: mongodb.cpp:121
armarx::armem::server::ltm::util::fs::directoryExists
bool directoryExists(const std::filesystem::path &p)
Definition: filesystem.cpp:130
armarx::armem::server::ltm::util::mongodb::toCollectionName
std::string toCollectionName(const armem::MemoryID &id)
Definition: mongodb.cpp:150
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::TYPE
static const constexpr char * TYPE
Definition: MongoDBStorageMixin.h:110
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::setHost
void setHost(const std::string &)
Definition: MongoDBStorageMixin.cpp:121
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::connected
bool connected() const
Definition: MongoDBStorageMixin.cpp:217
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::getDatabaseName
std::string getDatabaseName() const
Definition: MongoDBStorageMixin.cpp:256
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::ensureDatabaseExists
mongocxx::database ensureDatabaseExists(bool createIfNotExistent=false)
Definition: MongoDBStorageMixin.cpp:313
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
armarx::armem::server::ltm::detail::mixin
Definition: BufferedMemoryMixin.cpp:3
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::DATA
static const constexpr char * DATA
Definition: MongoDBStorageMixin.h:111
filesystem.h
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::MongoDBStorageMixin
MongoDBStorageMixin()=default
armarx::armem::server::ltm::detail::mixin::MongoDBSettings
Definition: MongoDBStorageMixin.h:15
armarx::armem::server::ltm::util::mongodb::collectionExists
std::optional< mongocxx::collection > collectionExists(mongocxx::database &db, const std::string &collectionName)
Definition: mongodb.cpp:111
Logging.h
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::setPort
void setPort(const unsigned int)
Definition: MongoDBStorageMixin.cpp:127
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::documentExists
std::optional< nlohmann::json > documentExists() const
Definition: MongoDBStorageMixin.cpp:293
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:186
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::readDataFromDocument
nlohmann::json readDataFromDocument() const
Definition: MongoDBStorageMixin.cpp:419
armarx::armem::server::ltm::detail::mixin::MongoDBSettings::toString
std::string toString() const
Definition: MongoDBStorageMixin.cpp:108
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::ID
static const constexpr char * ID
Definition: MongoDBStorageMixin.h:108
armarx::armem::server::ltm::util::mongodb::ensureDatabaseExists
mongocxx::database ensureDatabaseExists(mongocxx::client &client, const std::string &databaseName, bool createIfNotExistent)
Definition: mongodb.cpp:94
armarx::armem::server::ltm::detail::mixin::MongoDBSettings::user
std::string user
Definition: MongoDBStorageMixin.h:20
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::writeDataToDocument
void writeDataToDocument(const nlohmann::json &data)
Definition: MongoDBStorageMixin.cpp:413
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::getDocumentName
std::string getDocumentName() const
Definition: MongoDBStorageMixin.cpp:237
armarx::armem::server::ltm::detail::mixin::MongoDBStorageMixin::getPreviousCollectionName
std::string getPreviousCollectionName() const
Definition: MongoDBStorageMixin.cpp:249