PluginCache.cpp
Go to the documentation of this file.
1 #include "PluginCache.h"
2 
4 
8 
9 #include <QDateTime>
10 #include <QDirIterator>
11 #include <QFile>
12 #include <QResource>
13 #include <QCryptographicHash>
14 
15 namespace armarx
16 {
17 
19  cachePath(GetIconCachePath()),
20  manager(manager),
21  s(armarx::ArmarXDataPath::GetDefaultUserConfigPath().c_str() + settingsApplicationName, QSettings::NativeFormat)
22  {
24  QDir dir(cachePath);
25  if (!dir.exists())
26  {
27  dir.mkpath(".");
28  }
29 
30 
31  auto map = s.value("widgets").toMap();
32  for (QString key : map.keys())
33  {
34  QStringList w = map[key].toString().split(":");
35  QStringList keyparts = key.split(":");
36  if (w.size() == 3)
37  {
38  auto& elem = pluginData[keyparts[0]];
39  elem.pluginPath = keyparts[0];
40  elem.lastModified = QDateTime::fromMSecsSinceEpoch(w[0].toLongLong());
41  elem.hash = w[1].toLatin1();
42  ARMARX_INFO_S << "Widget: " << w[2];
43  elem.widgets[w[2]] = ArmarXWidgetInfoPtr();
44  }
45  }
46  }
47 
49  {
50  shutdown = true;
51  if (preloadFuture.valid())
52  {
53  preloadFuture.wait();
54  }
55  }
56 
57  bool PluginCache::cachePlugin(const QString& pluginPath)
58  {
59  if (pluginPath.isEmpty())
60  {
61  return false;
62  }
63  bool found = false;
64  QFileInfo info(pluginPath) ;
65  {
66  std::unique_lock lock(cacheMutex);
67  for (auto& elem : pluginData)
68  {
69  // ARMARX_INFO_S << info.lastModified().toString() << elem.pluginPath;
70  if (elem.pluginPath == pluginPath)
71  {
72  if (info.lastModified() == elem.lastModified)
73  {
74  ARMARX_DEBUG_S << "Same timestamp";
75  found = true;
76  }
77  else if (getHash(pluginPath) == elem.hash)
78  {
79  ARMARX_DEBUG_S << "Same hash - timestamps " << info.lastModified().toString() << " <> " << elem.lastModified.toString() ;
80  updateLastModifiedTimestamp(pluginPath);
81  found = true;
82  }
83  else
84  {
85  ARMARX_VERBOSE_S << "Plugin filestamp and hash is different - loading plugin again.";
86  }
87  break;
88  }
89  }
90  }
91  if (!found)
92  {
93  ARMARX_INFO_S << "Adding plugin " << pluginPath;
94  writeToCache(pluginPath);
95  return false;
96  }
97  ARMARX_INFO_S << "Found plugin " << pluginPath << " in cache";
98  return true;
99  }
100 
101  bool PluginCache::cacheWidget(QString widgetName, ArmarXWidgetInfoPtr widgetCreator)
102  {
103  std::unique_lock lock(cacheMutex);
104  pluginData[""].widgets[widgetName] = widgetCreator;
105  return true;
106  }
107 
109  {
110  std::unique_lock lock(cacheMutex);
111  for (const PluginData& data : pluginData)
112  {
113  WidgetCreatorMap::const_iterator it = data.widgets.find(widgetName);
114  if (it != data.widgets.end())
115  {
116  if (it->second)
117  {
118  return it->second;
119  }
120  }
121  }
122  PluginData data = loadFromCache(widgetName);
123  pluginData[data.pluginPath] = data;
124  WidgetCreatorMap::const_iterator it = data.widgets.find(widgetName);
125  if (it != data.widgets.end())
126  {
127  return it->second;
128  }
129  // widget does not exist (anymore) -> remove from cache file
130  removeWidgetFromCache(data.pluginPath, widgetName);
131  return ArmarXWidgetInfoPtr();
132  }
133 
135  {
136  std::unique_lock lock(cacheMutex);
137  QStringList result;
138  for (const PluginData& data : pluginData)
139  {
140  for (const auto& elem : data.widgets)
141  {
142  result << elem.first;
143  }
144  }
145  return result;
146 
147  }
148 
150  {
151  std::unique_lock lock(cacheMutex);
152  WidgetCreatorMap result;
153  for (const PluginData& data : pluginData)
154  {
155  result.insert(data.widgets.begin(), data.widgets.end());
156  }
157  return result;
158  }
159 
161  {
162  QDirIterator it(":", QDirIterator::Subdirectories);
163  while (it.hasNext())
164  {
165  QString path = it.next().remove(0, 1);
166  auto newPath = cachePath + "/resources/" + path;
167  QDir dir(newPath);
168  if (!dir.exists())
169  {
170  dir.mkpath(".");
171  }
172  if (!path.isEmpty() && !QFile::exists(newPath))
173  {
174  QFile::copy(it.next(), newPath);
175  }
176  }
177  }
178 
180  {
181  return QString(armarx::ArmarXDataPath::GetCachePath().c_str()) + "/icons/";
182  }
183 
184  QString PluginCache::GetIconPath(const QString& widgetName)
185  {
186  return GetIconCachePath() + widgetName + ".png";
187  }
188 
189  QString PluginCache::GetCategoryIconPath(const QString& widgetName)
190  {
191  return GetIconCachePath() + widgetName + "-category.png";
192  }
193 
194  bool PluginCache::ContainsAny(const QString& str, const QStringList& items)
195  {
196  for (const QString& item : items)
197  {
198  if (str.contains(item, Qt::CaseInsensitive))
199  {
200  return true;
201  }
202  }
203  return false;
204  }
205 
206  void PluginCache::preloadAsync(QStringList widgetNames, int delayMS)
207  {
208  widgetNames.removeDuplicates();
209  auto preload = [this](QStringList widgetNames, int delayMS)
210  {
211  QStringList blacklist = {"libPointCloudViewerGuiPlugin"};
212  usleep(delayMS * 1000);
213  for (QString widgetName : widgetNames)
214  {
215  {
216  std::unique_lock lock(cacheMutex);
217  QString pluginPath;
218  for (PluginData& data : pluginData)
219  {
220  if (ContainsAny(data.pluginPath, blacklist))
221  {
222  ARMARX_VERBOSE << deactivateSpam(10, data.pluginPath.toStdString()) << "skipping blacklisted plugin for preload: " << data.pluginPath;
223  continue;
224  }
225  for (auto& elem : data.widgets)
226  {
227  if (shutdown)
228  {
229  return;
230  }
231  if (widgetName == elem.first)
232  {
233  pluginPath = data.pluginPath;
234  if (!data.pluginLoader && !pluginPath.isEmpty())
235  {
236  auto pluginLoader = QSharedPointer<QPluginLoader>(new QPluginLoader(pluginPath));
237  ARMARX_VERBOSE_S << "Loading plugin " << pluginPath;
238  pluginLoader->load();
239  // data.pluginLoader->load();
240  break;
241  }
242  }
243  }
244  }
245  }
246  usleep(1000); // give others chance to get mutex
247  }
248  };
249  preloadFuture = std::async(std::launch::async, preload, widgetNames, delayMS);
250  }
251 
253  {
254  std::unique_lock lock(cacheMutex);
255  ARMARX_INFO << "Clearing plugin cache";
256  s.clear();
257  }
258 
259  QByteArray PluginCache::getHash(const QString& pluginPath)
260  {
261  QFile file(pluginPath);
262  QByteArray hashData;
263  if (file.open(QIODevice::ReadOnly))
264  {
265  QByteArray fileData = file.readAll();
266  hashData = QCryptographicHash::hash(fileData, QCryptographicHash::Md5);
267  }
268  return hashData.toHex();
269  }
270 
271  void PluginCache::removeWidgetFromCache(QString pluginPath, QString widgetName)
272  {
273  std::unique_lock lock(cacheMutex);
274  QMap<QString, QVariant> widgetMap = s.value("widgets").toMap();
275  widgetMap.remove(pluginPath + ":" + widgetName);
276  s.setValue("widgets", widgetMap);
277 
278  QMap<QString, PluginData> newPluginDataMap;
279  for (auto& key : pluginData.keys())
280  {
281  if (pluginData[key].pluginPath != pluginPath && pluginData[key].widgets.count(widgetName) == 0)
282  {
283  newPluginDataMap[key] = pluginData[key];
284  }
285  }
286  pluginData.swap(newPluginDataMap);
287  }
288 
289  void PluginCache::removePluginFromCache(QString pluginPath)
290  {
291  std::unique_lock lock(cacheMutex);
292  ARMARX_INFO << "Removing plugin " << pluginPath << " from cache";
293  QMap<QString, QVariant> widgetMap = s.value("widgets").toMap();
294  QMap<QString, QVariant> newWidgetMap;
295  for (auto key : widgetMap.keys())
296  {
297  if (!key.contains(pluginPath))
298  {
299  newWidgetMap[key] = widgetMap[key];
300  }
301  }
302  QMap<QString, PluginData> newPluginDataMap;
303  for (auto& key : pluginData.keys())
304  {
305  if (pluginData[key].pluginPath != pluginPath)
306  {
307  newPluginDataMap[key] = pluginData[key];
308  }
309  }
310  pluginData.swap(newPluginDataMap);
311  s.setValue("widgets", widgetMap);
312  }
313 
314  void PluginCache::updateLastModifiedTimestamp(const QString& pluginPath)
315  {
316  QDateTime time = QFileInfo(pluginPath).lastModified();
317  std::unique_lock lock(cacheMutex);
318  QMap<QString, QVariant> widgetMap = s.value("widgets").toMap();
319  ARMARX_DEBUG_S << "Updating last modified timestamp of plugin " << pluginPath << " to " << time.toString();
320  auto& pluginDataElement = pluginData[pluginPath];
321  for (auto& widget : pluginDataElement.widgets)
322  {
323  widgetMap[pluginPath + ":" + widget.first] = QString::number(time.toMSecsSinceEpoch()) + ":" + pluginDataElement.hash + ":" + widget.first;
324  }
325 
326  assert(QString::number(time.toMSecsSinceEpoch()).toLongLong() == time.toMSecsSinceEpoch());
327 
328  pluginDataElement.lastModified = time;
329  s.setValue("widgets", widgetMap);
330  }
331 
332  void PluginCache::writeToCache(const QString& pluginPath)
333  {
334  auto start = IceUtil::Time::now();
335  QByteArray hashData = getHash(pluginPath);
336  ARMARX_DEBUG_S << "Hashing took " << (IceUtil::Time::now() - start).toMicroSecondsDouble();
337  QSharedPointer<QPluginLoader> loader(new QPluginLoader(pluginPath));
338  auto widgets = loadPlugin(loader);
340  ARMARX_VERBOSE_S << "Writing plugin " << pluginPath << " to cache";
341  std::unique_lock lock(cacheMutex);
342  QMap<QString, QVariant> widgetMap = s.value("widgets").toMap();
343  QDateTime time = QFileInfo(pluginPath).lastModified();
344  for (auto& elem : widgets)
345  {
346  widgetMap[pluginPath + ":" + elem.first] = QString::number(time.toMSecsSinceEpoch()) + ":" + hashData + ":" + elem.first;
347  }
348 
349  s.setValue("widgets", widgetMap);
350 
351 
352  pluginData[pluginPath] = {loader, pluginPath, hashData, time, widgets};
353 
354  }
355 
356  PluginData PluginCache::loadFromCache(const QString& widgetName)
357  {
358  ARMARX_INFO_S << "Loading widget " << widgetName << " from cache ";
359 
360  QString pluginPath;
361  QSharedPointer<QPluginLoader> loader;
362  WidgetCreatorMap widgets;
363  QDateTime time;
364  QByteArray hash;
365  QMap<QString, QVariant> map;
366  {
367  std::unique_lock lock(cacheMutex);
368  map = s.value("widgets").toMap();
369  }
370  for (auto key : map.keys())
371  {
372  QStringList w = map[key].toString().split(":");
373  QStringList keyparts = key.split(":");
374  if (w.size() == 3)
375  {
376  if (w[2] == widgetName)
377  {
378  pluginPath = keyparts[0];
379  if (QFile::exists(pluginPath))
380  {
381  hash = w[1].toLatin1();
382  loader = QSharedPointer<QPluginLoader>(new QPluginLoader(pluginPath));
383  widgets = loadPlugin(loader);
384  time = QDateTime::fromMSecsSinceEpoch(w[0].toLongLong());
385  break;
386  }
387  else
388  {
389  ARMARX_INFO_S << pluginPath << " does not exist";
390  }
391 
392  }
393  }
394 
395  }
396  if (!loader)
397  {
398  ARMARX_WARNING_S << "Could not find " << widgetName << " in cache";
399  }
400 
401  return PluginData {loader, pluginPath, hash, time, widgets};
402  }
403 
404  WidgetCreatorMap PluginCache::loadPlugin(QSharedPointer<QPluginLoader> loader)
405  {
406  WidgetCreatorMap result;
407  ARMARX_VERBOSE_S << "Loading plugin " << loader->fileName();
408  QObject* plugin = loader->instance(); // calls load
409 
410  if (plugin)
411  {
412  ArmarXGuiInterface* newPlugin = qobject_cast<ArmarXGuiInterface*>(plugin);
413  if (newPlugin)
414  {
415  //ARMARX_INFO << "cast successful" << armarx::flush;
416  WidgetCreatorMap newWidgets = newPlugin->getProvidedWidgets();
417  for (auto elem : newWidgets)
418  {
419  ArmarXWidgetInfoPtr info = elem.second;
420  QIcon icon = info->getIcon();
421  if (!icon.isNull())
422  {
423  icon.pixmap(256, 64).save(GetIconPath(elem.first));
424  }
425  icon = info->getCategoryIcon();
426  if (!icon.isNull())
427  {
428  icon.pixmap(256, 64).save(GetCategoryIconPath(elem.first));
429  }
430  }
431  result.insert(newWidgets.begin(), newWidgets.end());
432  if (manager)
433  {
434  manager->registerKnownObjectFactoriesWithIce();
435  }
436  else
437  {
438  ARMARX_WARNING << "ARMARX Manager is NULL";
439  }
440 
441  }
442  else
443  {
444  ARMARX_WARNING << "plugin instance is NULL";
445  }
446  }
447  else
448  {
449  ARMARX_WARNING_S << "Instantiating plugin failed: " << loader->fileName()
450  << "\nError string:\n" << loader->errorString().toStdString();
451  }
452  return result;
453  }
454 
455 } // namespace armarx
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:180
armarx::PluginCache::GetCategoryIconPath
static QString GetCategoryIconPath(const QString &widgetName)
Definition: PluginCache.cpp:189
str
std::string str(const T &t)
Definition: UserAssistedSegmenterGuiWidgetController.cpp:42
armarx::PluginCache::GetIconPath
static QString GetIconPath(const QString &widgetName)
Definition: PluginCache.cpp:184
armarx::PluginData
Definition: PluginCache.h:27
ArmarXGuiInterface.h
ArmarXManager.h
armarx::PluginCache::shutdown
bool shutdown
Definition: PluginCache.h:70
armarx::PluginCache::preloadFuture
std::future< void > preloadFuture
Definition: PluginCache.h:69
ArmarXWidgetInfoPtr
std::shared_ptr< ArmarXWidgetInfo > ArmarXWidgetInfoPtr
Definition: ArmarXGuiInterface.h:32
armarx::PluginCache::clearCacheFile
void clearCacheFile()
Definition: PluginCache.cpp:252
armarx::PluginCache::preloadAsync
void preloadAsync(QStringList widgetNames, int delayMS=1000)
Definition: PluginCache.cpp:206
armarx::PluginCache::cacheMutex
std::recursive_mutex cacheMutex
Definition: PluginCache.h:68
ArmarXGuiInterface
The main gui interface.
Definition: ArmarXGuiInterface.h:74
deactivateSpam
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition: Logging.cpp:72
armarx::PluginCache::copyResourcesToCache
void copyResourcesToCache()
Definition: PluginCache.cpp:160
armarx::PluginCache::cacheWidget
bool cacheWidget(QString widgetName, ArmarXWidgetInfoPtr widgetCreator)
Definition: PluginCache.cpp:101
ARMARX_DEBUG_S
#define ARMARX_DEBUG_S
Definition: Logging.h:198
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:734
armarx::PluginCache::getWidgetCreator
ArmarXWidgetInfoPtr getWidgetCreator(const QString &widgetName)
Definition: PluginCache.cpp:108
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
armarx::PluginCache::removePluginFromCache
void removePluginFromCache(QString pluginPath)
Definition: PluginCache.cpp:289
armarx::ArmarXDataPath
Definition: ArmarXDataPath.h:57
armarx::PluginCache::getAvailableWidgets
WidgetCreatorMap getAvailableWidgets() const
Definition: PluginCache.cpp:149
armarx::PluginCache::updateLastModifiedTimestamp
void updateLastModifiedTimestamp(const QString &pluginPath)
Definition: PluginCache.cpp:314
ARMARX_WARNING_S
#define ARMARX_WARNING_S
Definition: Logging.h:206
armarx::PluginCache::pluginData
QMap< QString, PluginData > pluginData
Definition: PluginCache.h:65
ExpressionException.h
armarx::PluginCache::manager
ArmarXManagerPtr manager
Definition: PluginCache.h:66
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:259
WidgetCreatorMap
std::map< QString, ArmarXWidgetInfoPtr > WidgetCreatorMap
Definition: ArmarXGuiInterface.h:35
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
armarx::PluginCache::ContainsAny
static bool ContainsAny(const QString &str, const QStringList &items)
Definition: PluginCache.cpp:194
IceUtil::Handle< ArmarXManager >
armarx::PluginCache::GetIconCachePath
static QString GetIconCachePath()
Definition: PluginCache.cpp:179
armarx::PluginCache::loadFromCache
PluginData loadFromCache(const QString &pluginPath)
Definition: PluginCache.cpp:356
armarx::PluginCache::cachePath
const QString cachePath
Definition: PluginCache.h:64
armarx::PluginCache::s
QSettings s
Definition: PluginCache.h:67
armarx::PluginCache::writeToCache
void writeToCache(const QString &pluginPath)
Definition: PluginCache.cpp:332
armarx::PluginCache::loadPlugin
WidgetCreatorMap loadPlugin(QSharedPointer< QPluginLoader > loader)
Definition: PluginCache.cpp:404
armarx::PluginCache::removeWidgetFromCache
void removeWidgetFromCache(QString pluginPath, QString widgetName)
Definition: PluginCache.cpp:271
ARMARX_VERBOSE_S
#define ARMARX_VERBOSE_S
Definition: Logging.h:200
ARMARX_INFO_S
#define ARMARX_INFO_S
Definition: Logging.h:195
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:186
armarx::PluginCache::getAvailableWidgetNames
QStringList getAvailableWidgetNames() const
Definition: PluginCache.cpp:134
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:28
armarx::PluginCache::~PluginCache
~PluginCache()
Definition: PluginCache.cpp:48
armarx::PluginCache::PluginCache
PluginCache(ArmarXManagerPtr manager)
Definition: PluginCache.cpp:18
armarx::PluginCache::cachePlugin
bool cachePlugin(const QString &pluginPath)
Definition: PluginCache.cpp:57