PluginCache.cpp
Go to the documentation of this file.
1 #include "PluginCache.h"
2 
3 #include <QCryptographicHash>
4 #include <QDateTime>
5 #include <QDirIterator>
6 #include <QFile>
7 #include <QResource>
8 
12 
14 
15 namespace armarx
16 {
17 
19  cachePath(GetIconCachePath()),
20  manager(manager),
21  s(armarx::ArmarXDataPath::GetDefaultUserConfigPath().c_str() + settingsApplicationName,
22  QSettings::NativeFormat)
23  {
25  QDir dir(cachePath);
26  if (!dir.exists())
27  {
28  dir.mkpath(".");
29  }
30 
31 
32  auto map = s.value("widgets").toMap();
33  for (QString key : map.keys())
34  {
35  QStringList w = map[key].toString().split(":");
36  QStringList keyparts = key.split(":");
37  if (w.size() == 3)
38  {
39  auto& elem = pluginData[keyparts[0]];
40  elem.pluginPath = keyparts[0];
41  elem.lastModified = QDateTime::fromMSecsSinceEpoch(w[0].toLongLong());
42  elem.hash = w[1].toLatin1();
43  ARMARX_INFO_S << "Widget: " << w[2];
44  elem.widgets[w[2]] = ArmarXWidgetInfoPtr();
45  }
46  }
47  }
48 
50  {
51  shutdown = true;
52  if (preloadFuture.valid())
53  {
54  preloadFuture.wait();
55  }
56  }
57 
58  bool
59  PluginCache::cachePlugin(const QString& pluginPath)
60  {
61  if (pluginPath.isEmpty())
62  {
63  return false;
64  }
65  bool found = false;
66  QFileInfo info(pluginPath);
67  {
68  std::unique_lock lock(cacheMutex);
69  for (auto& elem : pluginData)
70  {
71  // ARMARX_INFO_S << info.lastModified().toString() << elem.pluginPath;
72  if (elem.pluginPath == pluginPath)
73  {
74  if (info.lastModified() == elem.lastModified)
75  {
76  ARMARX_DEBUG_S << "Same timestamp";
77  found = true;
78  }
79  else if (getHash(pluginPath) == elem.hash)
80  {
81  ARMARX_DEBUG_S << "Same hash - timestamps "
82  << info.lastModified().toString() << " <> "
83  << elem.lastModified.toString();
84  updateLastModifiedTimestamp(pluginPath);
85  found = true;
86  }
87  else
88  {
90  << "Plugin filestamp and hash is different - loading plugin again.";
91  }
92  break;
93  }
94  }
95  }
96  if (!found)
97  {
98  ARMARX_INFO_S << "Adding plugin " << pluginPath;
99  writeToCache(pluginPath);
100  return false;
101  }
102  ARMARX_INFO_S << "Found plugin " << pluginPath << " in cache";
103  return true;
104  }
105 
106  bool
107  PluginCache::cacheWidget(QString widgetName, ArmarXWidgetInfoPtr widgetCreator)
108  {
109  std::unique_lock lock(cacheMutex);
110  pluginData[""].widgets[widgetName] = widgetCreator;
111  return true;
112  }
113 
115  PluginCache::getWidgetCreator(const QString& widgetName)
116  {
117  std::unique_lock lock(cacheMutex);
118  for (const PluginData& data : pluginData)
119  {
120  WidgetCreatorMap::const_iterator it = data.widgets.find(widgetName);
121  if (it != data.widgets.end())
122  {
123  if (it->second)
124  {
125  return it->second;
126  }
127  }
128  }
129  PluginData data = loadFromCache(widgetName);
130  pluginData[data.pluginPath] = data;
131  WidgetCreatorMap::const_iterator it = data.widgets.find(widgetName);
132  if (it != data.widgets.end())
133  {
134  return it->second;
135  }
136  // widget does not exist (anymore) -> remove from cache file
137  removeWidgetFromCache(data.pluginPath, widgetName);
138  return ArmarXWidgetInfoPtr();
139  }
140 
141  QStringList
143  {
144  std::unique_lock lock(cacheMutex);
145  QStringList result;
146  for (const PluginData& data : pluginData)
147  {
148  for (const auto& elem : data.widgets)
149  {
150  result << elem.first;
151  }
152  }
153  return result;
154  }
155 
158  {
159  std::unique_lock lock(cacheMutex);
160  WidgetCreatorMap result;
161  for (const PluginData& data : pluginData)
162  {
163  result.insert(data.widgets.begin(), data.widgets.end());
164  }
165  return result;
166  }
167 
168  void
170  {
171  QDirIterator it(":", QDirIterator::Subdirectories);
172  while (it.hasNext())
173  {
174  QString path = it.next().remove(0, 1);
175  auto newPath = cachePath + "/resources/" + path;
176  QDir dir(newPath);
177  if (!dir.exists())
178  {
179  dir.mkpath(".");
180  }
181  if (!path.isEmpty() && !QFile::exists(newPath))
182  {
183  QFile::copy(it.next(), newPath);
184  }
185  }
186  }
187 
188  QString
190  {
191  return QString(armarx::ArmarXDataPath::GetCachePath().c_str()) + "/icons/";
192  }
193 
194  QString
195  PluginCache::GetIconPath(const QString& widgetName)
196  {
197  return GetIconCachePath() + widgetName + ".png";
198  }
199 
200  QString
201  PluginCache::GetCategoryIconPath(const QString& widgetName)
202  {
203  return GetIconCachePath() + widgetName + "-category.png";
204  }
205 
206  bool
207  PluginCache::ContainsAny(const QString& str, const QStringList& items)
208  {
209  for (const QString& item : items)
210  {
211  if (str.contains(item, Qt::CaseInsensitive))
212  {
213  return true;
214  }
215  }
216  return false;
217  }
218 
219  void
220  PluginCache::preloadAsync(QStringList widgetNames, int delayMS)
221  {
222  widgetNames.removeDuplicates();
223  auto preload = [this](QStringList widgetNames, int delayMS)
224  {
225  QStringList blacklist = {"libPointCloudViewerGuiPlugin"};
226  usleep(delayMS * 1000);
227  for (QString widgetName : widgetNames)
228  {
229  {
230  std::unique_lock lock(cacheMutex);
231  QString pluginPath;
232  for (PluginData& data : pluginData)
233  {
234  if (ContainsAny(data.pluginPath, blacklist))
235  {
237  << deactivateSpam(10, data.pluginPath.toStdString())
238  << "skipping blacklisted plugin for preload: " << data.pluginPath;
239  continue;
240  }
241  for (auto& elem : data.widgets)
242  {
243  if (shutdown)
244  {
245  return;
246  }
247  if (widgetName == elem.first)
248  {
249  pluginPath = data.pluginPath;
250  if (!data.pluginLoader && !pluginPath.isEmpty())
251  {
252  auto pluginLoader = QSharedPointer<QPluginLoader>(
253  new QPluginLoader(pluginPath));
254  ARMARX_VERBOSE_S << "Loading plugin " << pluginPath;
255  pluginLoader->load();
256  // data.pluginLoader->load();
257  break;
258  }
259  }
260  }
261  }
262  }
263  usleep(1000); // give others chance to get mutex
264  }
265  };
266  preloadFuture = std::async(std::launch::async, preload, widgetNames, delayMS);
267  }
268 
269  void
271  {
272  std::unique_lock lock(cacheMutex);
273  ARMARX_INFO << "Clearing plugin cache";
274  s.clear();
275  }
276 
277  QByteArray
278  PluginCache::getHash(const QString& pluginPath)
279  {
280  QFile file(pluginPath);
281  QByteArray hashData;
282  if (file.open(QIODevice::ReadOnly))
283  {
284  QByteArray fileData = file.readAll();
285  hashData = QCryptographicHash::hash(fileData, QCryptographicHash::Md5);
286  }
287  return hashData.toHex();
288  }
289 
290  void
291  PluginCache::removeWidgetFromCache(QString pluginPath, QString widgetName)
292  {
293  std::unique_lock lock(cacheMutex);
294  QMap<QString, QVariant> widgetMap = s.value("widgets").toMap();
295  widgetMap.remove(pluginPath + ":" + widgetName);
296  s.setValue("widgets", widgetMap);
297 
298  QMap<QString, PluginData> newPluginDataMap;
299  for (auto& key : pluginData.keys())
300  {
301  if (pluginData[key].pluginPath != pluginPath &&
302  pluginData[key].widgets.count(widgetName) == 0)
303  {
304  newPluginDataMap[key] = pluginData[key];
305  }
306  }
307  pluginData.swap(newPluginDataMap);
308  }
309 
310  void
312  {
313  std::unique_lock lock(cacheMutex);
314  ARMARX_INFO << "Removing plugin " << pluginPath << " from cache";
315  QMap<QString, QVariant> widgetMap = s.value("widgets").toMap();
316  QMap<QString, QVariant> newWidgetMap;
317  for (auto key : widgetMap.keys())
318  {
319  if (!key.contains(pluginPath))
320  {
321  newWidgetMap[key] = widgetMap[key];
322  }
323  }
324  QMap<QString, PluginData> newPluginDataMap;
325  for (auto& key : pluginData.keys())
326  {
327  if (pluginData[key].pluginPath != pluginPath)
328  {
329  newPluginDataMap[key] = pluginData[key];
330  }
331  }
332  pluginData.swap(newPluginDataMap);
333  s.setValue("widgets", widgetMap);
334  }
335 
336  void
337  PluginCache::updateLastModifiedTimestamp(const QString& pluginPath)
338  {
339  QDateTime time = QFileInfo(pluginPath).lastModified();
340  std::unique_lock lock(cacheMutex);
341  QMap<QString, QVariant> widgetMap = s.value("widgets").toMap();
342  ARMARX_DEBUG_S << "Updating last modified timestamp of plugin " << pluginPath << " to "
343  << time.toString();
344  auto& pluginDataElement = pluginData[pluginPath];
345  for (auto& widget : pluginDataElement.widgets)
346  {
347  widgetMap[pluginPath + ":" + widget.first] = QString::number(time.toMSecsSinceEpoch()) +
348  ":" + pluginDataElement.hash + ":" +
349  widget.first;
350  }
351 
352  assert(QString::number(time.toMSecsSinceEpoch()).toLongLong() == time.toMSecsSinceEpoch());
353 
354  pluginDataElement.lastModified = time;
355  s.setValue("widgets", widgetMap);
356  }
357 
358  void
359  PluginCache::writeToCache(const QString& pluginPath)
360  {
361  auto start = IceUtil::Time::now();
362  QByteArray hashData = getHash(pluginPath);
363  ARMARX_DEBUG_S << "Hashing took " << (IceUtil::Time::now() - start).toMicroSecondsDouble();
364  QSharedPointer<QPluginLoader> loader(new QPluginLoader(pluginPath));
365  auto widgets = loadPlugin(loader);
367  ARMARX_VERBOSE_S << "Writing plugin " << pluginPath << " to cache";
368  std::unique_lock lock(cacheMutex);
369  QMap<QString, QVariant> widgetMap = s.value("widgets").toMap();
370  QDateTime time = QFileInfo(pluginPath).lastModified();
371  for (auto& elem : widgets)
372  {
373  widgetMap[pluginPath + ":" + elem.first] =
374  QString::number(time.toMSecsSinceEpoch()) + ":" + hashData + ":" + elem.first;
375  }
376 
377  s.setValue("widgets", widgetMap);
378 
379 
380  pluginData[pluginPath] = {loader, pluginPath, hashData, time, widgets};
381  }
382 
383  PluginData
384  PluginCache::loadFromCache(const QString& widgetName)
385  {
386  ARMARX_INFO_S << "Loading widget " << widgetName << " from cache ";
387 
388  QString pluginPath;
389  QSharedPointer<QPluginLoader> loader;
390  WidgetCreatorMap widgets;
391  QDateTime time;
392  QByteArray hash;
393  QMap<QString, QVariant> map;
394  {
395  std::unique_lock lock(cacheMutex);
396  map = s.value("widgets").toMap();
397  }
398  for (auto key : map.keys())
399  {
400  QStringList w = map[key].toString().split(":");
401  QStringList keyparts = key.split(":");
402  if (w.size() == 3)
403  {
404  if (w[2] == widgetName)
405  {
406  pluginPath = keyparts[0];
407  if (QFile::exists(pluginPath))
408  {
409  hash = w[1].toLatin1();
410  loader = QSharedPointer<QPluginLoader>(new QPluginLoader(pluginPath));
411  widgets = loadPlugin(loader);
412  time = QDateTime::fromMSecsSinceEpoch(w[0].toLongLong());
413  break;
414  }
415  else
416  {
417  ARMARX_INFO_S << pluginPath << " does not exist";
418  }
419  }
420  }
421  }
422  if (!loader)
423  {
424  ARMARX_WARNING_S << "Could not find " << widgetName << " in cache";
425  }
426 
427  return PluginData{loader, pluginPath, hash, time, widgets};
428  }
429 
431  PluginCache::loadPlugin(QSharedPointer<QPluginLoader> loader)
432  {
433  WidgetCreatorMap result;
434  ARMARX_VERBOSE_S << "Loading plugin " << loader->fileName();
435  QObject* plugin = loader->instance(); // calls load
436 
437  if (plugin)
438  {
439  ArmarXGuiInterface* newPlugin = qobject_cast<ArmarXGuiInterface*>(plugin);
440  if (newPlugin)
441  {
442  //ARMARX_INFO << "cast successful" << armarx::flush;
443  WidgetCreatorMap newWidgets = newPlugin->getProvidedWidgets();
444  for (auto elem : newWidgets)
445  {
446  ArmarXWidgetInfoPtr info = elem.second;
447  QIcon icon = info->getIcon();
448  if (!icon.isNull())
449  {
450  icon.pixmap(256, 64).save(GetIconPath(elem.first));
451  }
452  icon = info->getCategoryIcon();
453  if (!icon.isNull())
454  {
455  icon.pixmap(256, 64).save(GetCategoryIconPath(elem.first));
456  }
457  }
458  result.insert(newWidgets.begin(), newWidgets.end());
459  if (manager)
460  {
461  manager->registerKnownObjectFactoriesWithIce();
462  }
463  else
464  {
465  ARMARX_WARNING << "ARMARX Manager is NULL";
466  }
467  }
468  else
469  {
470  ARMARX_WARNING << "plugin instance is NULL";
471  }
472  }
473  else
474  {
475  ARMARX_WARNING_S << "Instantiating plugin failed: " << loader->fileName()
476  << "\nError string:\n"
477  << loader->errorString().toStdString();
478  }
479  return result;
480  }
481 
482 } // namespace armarx
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:187
armarx::PluginCache::GetCategoryIconPath
static QString GetCategoryIconPath(const QString &widgetName)
Definition: PluginCache.cpp:201
str
std::string str(const T &t)
Definition: UserAssistedSegmenterGuiWidgetController.cpp:43
armarx::PluginCache::GetIconPath
static QString GetIconPath(const QString &widgetName)
Definition: PluginCache.cpp:195
armarx::PluginData
Definition: PluginCache.h:27
ArmarXGuiInterface.h
ArmarXManager.h
armarx::PluginCache::shutdown
bool shutdown
Definition: PluginCache.h:71
armarx::PluginCache::preloadFuture
std::future< void > preloadFuture
Definition: PluginCache.h:70
ArmarXWidgetInfoPtr
std::shared_ptr< ArmarXWidgetInfo > ArmarXWidgetInfoPtr
Definition: ArmarXGuiInterface.h:32
armarx::PluginCache::clearCacheFile
void clearCacheFile()
Definition: PluginCache.cpp:270
armarx::PluginCache::preloadAsync
void preloadAsync(QStringList widgetNames, int delayMS=1000)
Definition: PluginCache.cpp:220
armarx::PluginCache::cacheMutex
std::recursive_mutex cacheMutex
Definition: PluginCache.h:69
ArmarXGuiInterface
The main gui interface.
Definition: ArmarXGuiInterface.h:80
deactivateSpam
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition: Logging.cpp:75
armarx::PluginCache::copyResourcesToCache
void copyResourcesToCache()
Definition: PluginCache.cpp:169
WidgetCreatorMap
std::map< QString, ArmarXWidgetInfoPtr > WidgetCreatorMap
Definition: ArmarXGuiInterface.h:35
armarx::PluginCache::cacheWidget
bool cacheWidget(QString widgetName, ArmarXWidgetInfoPtr widgetCreator)
Definition: PluginCache.cpp:107
ARMARX_DEBUG_S
#define ARMARX_DEBUG_S
Definition: Logging.h:205
copy
Use of this software is granted under one of the following two to be chosen freely by the user Boost Software License Version Marcin Kalicinski Permission is hereby free of to any person or organization obtaining a copy of the software and accompanying documentation covered by this and transmit the and to prepare derivative works of the and to permit third parties to whom the Software is furnished to do all subject to the including the above license this restriction and the following must be included in all copies of the in whole or in and all derivative works of the unless such copies or derivative works are solely in the form of machine executable object code generated by a source language processor THE SOFTWARE IS PROVIDED AS WITHOUT WARRANTY OF ANY EXPRESS OR INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF FITNESS FOR A PARTICULAR TITLE AND NON INFRINGEMENT IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER WHETHER IN TORT OR ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE The MIT Marcin Kalicinski Permission is hereby free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to copy
Definition: license.txt:39
armarx::ArmarXDataPath::GetCachePath
static std::string GetCachePath()
The base Cache directory of ArmarX.
Definition: ArmarXDataPath.cpp:729
armarx::PluginCache::getWidgetCreator
ArmarXWidgetInfoPtr getWidgetCreator(const QString &widgetName)
Definition: PluginCache.cpp:115
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
armarx::PluginCache::removePluginFromCache
void removePluginFromCache(QString pluginPath)
Definition: PluginCache.cpp:311
armarx::ArmarXDataPath
Definition: ArmarXDataPath.h:56
armarx::PluginCache::getAvailableWidgets
WidgetCreatorMap getAvailableWidgets() const
Definition: PluginCache.cpp:157
armarx::PluginCache::updateLastModifiedTimestamp
void updateLastModifiedTimestamp(const QString &pluginPath)
Definition: PluginCache.cpp:337
ARMARX_WARNING_S
#define ARMARX_WARNING_S
Definition: Logging.h:213
armarx::PluginCache::pluginData
QMap< QString, PluginData > pluginData
Definition: PluginCache.h:66
ExpressionException.h
armarx::PluginCache::manager
ArmarXManagerPtr manager
Definition: PluginCache.h:67
ArmarXGuiInterface::getProvidedWidgets
virtual WidgetCreatorMap getProvidedWidgets()=0
ARMARX_CHECK_EXPRESSION
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
Definition: ExpressionException.h:73
armarx::PluginCache::getHash
QByteArray getHash(const QString &pluginPath)
Definition: PluginCache.cpp:278
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:181
armarx::PluginCache::ContainsAny
static bool ContainsAny(const QString &str, const QStringList &items)
Definition: PluginCache.cpp:207
IceUtil::Handle< ArmarXManager >
armarx::PluginCache::GetIconCachePath
static QString GetIconCachePath()
Definition: PluginCache.cpp:189
armarx::PluginCache::loadFromCache
PluginData loadFromCache(const QString &pluginPath)
Definition: PluginCache.cpp:384
armarx::PluginCache::cachePath
const QString cachePath
Definition: PluginCache.h:65
armarx::PluginCache::s
QSettings s
Definition: PluginCache.h:68
armarx::PluginCache::writeToCache
void writeToCache(const QString &pluginPath)
Definition: PluginCache.cpp:359
armarx::PluginCache::loadPlugin
WidgetCreatorMap loadPlugin(QSharedPointer< QPluginLoader > loader)
Definition: PluginCache.cpp:431
armarx::PluginCache::removeWidgetFromCache
void removeWidgetFromCache(QString pluginPath, QString widgetName)
Definition: PluginCache.cpp:291
ARMARX_VERBOSE_S
#define ARMARX_VERBOSE_S
Definition: Logging.h:207
ARMARX_INFO_S
#define ARMARX_INFO_S
Definition: Logging.h:202
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:193
armarx::PluginCache::getAvailableWidgetNames
QStringList getAvailableWidgetNames() const
Definition: PluginCache.cpp:142
ArmarXDataPath.h
PluginCache.h
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:33
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
armarx::PluginCache::~PluginCache
~PluginCache()
Definition: PluginCache.cpp:49
armarx::PluginCache::PluginCache
PluginCache(ArmarXManagerPtr manager)
Definition: PluginCache.cpp:18
armarx::PluginCache::cachePlugin
bool cachePlugin(const QString &pluginPath)
Definition: PluginCache.cpp:59