ctkPluginFrameworkContext.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /*=============================================================================
  2. Library: CTK
  3. Copyright (c) German Cancer Research Center,
  4. Division of Medical and Biological Informatics
  5. Licensed under the Apache License, Version 2.0 (the "License");
  6. you may not use this file except in compliance with the License.
  7. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. =============================================================================*/
  15. #include "ctkPluginFrameworkContext_p.h"
  16. #include "ctkPluginFrameworkUtil_p.h"
  17. #include "ctkPluginFramework_p.h"
  18. #include "ctkPluginFrameworkProperties_p.h"
  19. #include "ctkPluginArchive_p.h"
  20. #include "ctkPluginStorageSQL_p.h"
  21. #include "ctkPluginConstants.h"
  22. #include "ctkLocationManager_p.h"
  23. #include "ctkBasicLocation_p.h"
  24. #include "ctkServices_p.h"
  25. #include "ctkUtils.h"
  26. //----------------------------------------------------------------------------
  27. QMutex ctkPluginFrameworkContext::globalFwLock;
  28. int ctkPluginFrameworkContext::globalId = 1;
  29. //----------------------------------------------------------------------------
  30. ctkPluginFrameworkContext::ctkPluginFrameworkContext()
  31. : plugins(0), listeners(this), services(0), systemPlugin(new ctkPluginFramework()),
  32. storage(0), firstInit(true), props(ctkPluginFrameworkProperties::getProperties()),
  33. initialized(false)
  34. {
  35. {
  36. QMutexLocker lock(&globalFwLock);
  37. id = globalId++;
  38. systemPlugin->ctkPlugin::init(new ctkPluginFrameworkPrivate(systemPlugin, this));
  39. }
  40. initProperties();
  41. log() << "created";
  42. }
  43. //----------------------------------------------------------------------------
  44. ctkPluginFrameworkContext::~ctkPluginFrameworkContext()
  45. {
  46. if (initialized)
  47. {
  48. this->uninit();
  49. }
  50. }
  51. //----------------------------------------------------------------------------
  52. void ctkPluginFrameworkContext::initProperties()
  53. {
  54. props[ctkPluginConstants::FRAMEWORK_VERSION] = "0.9";
  55. props[ctkPluginConstants::FRAMEWORK_VENDOR] = "CommonTK";
  56. }
  57. //----------------------------------------------------------------------------
  58. void ctkPluginFrameworkContext::init()
  59. {
  60. log() << "initializing";
  61. if (debug.framework)
  62. {
  63. ctkBasicLocation* location = ctkLocationManager::getConfigurationLocation();
  64. if (location)
  65. {
  66. log() << "Configuration location:" << location->getUrl().toString();
  67. }
  68. location = ctkLocationManager::getInstallLocation();
  69. if (location)
  70. {
  71. log() << "Install location:" << location->getUrl().toString();
  72. }
  73. location = ctkLocationManager::getCTKHomeLocation();
  74. if (location)
  75. {
  76. log() << "CTK Home location:" << location->getUrl().toString();
  77. }
  78. location= ctkLocationManager::getUserLocation();
  79. if (location)
  80. {
  81. log() << "User location:" << location->getUrl().toString();
  82. }
  83. location = ctkLocationManager::getInstanceLocation();
  84. if (location)
  85. {
  86. log() << "Instance location" << location->getUrl().toString();
  87. }
  88. }
  89. if (firstInit && ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT
  90. == props[ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN])
  91. {
  92. deleteFWDir();
  93. firstInit = false;
  94. }
  95. // Pre-load libraries
  96. // This may speed up installing new plug-ins if they have dependencies on
  97. // one of these libraries. It prevents repeated loading and unloading of the
  98. // pre-loaded libraries during caching of the plug-in meta-data.
  99. if (props[ctkPluginConstants::FRAMEWORK_PRELOAD_LIBRARIES].isValid())
  100. {
  101. QStringList preloadLibs = props[ctkPluginConstants::FRAMEWORK_PRELOAD_LIBRARIES].toStringList();
  102. QLibrary::LoadHints loadHints;
  103. QVariant loadHintsVariant = props[ctkPluginConstants::FRAMEWORK_PLUGIN_LOAD_HINTS];
  104. if (loadHintsVariant.isValid())
  105. {
  106. loadHints = loadHintsVariant.value<QLibrary::LoadHints>();
  107. }
  108. foreach(QString preloadLib, preloadLibs)
  109. {
  110. QLibrary lib;
  111. QStringList nameAndVersion = preloadLib.split("$");
  112. QString libraryName;
  113. if (nameAndVersion.count() == 1)
  114. {
  115. libraryName = nameAndVersion.front();
  116. lib.setFileName(nameAndVersion.front());
  117. }
  118. else if (nameAndVersion.count() == 2)
  119. {
  120. libraryName = nameAndVersion.front() + "." + nameAndVersion.back();
  121. lib.setFileNameAndVersion(nameAndVersion.front(), nameAndVersion.back());
  122. }
  123. else
  124. {
  125. qWarning() << "Wrong syntax in" << preloadLib << ". Use <lib-name>[$version]. Skipping.";
  126. continue;
  127. }
  128. lib.setLoadHints(loadHints);
  129. log() << "Pre-loading library" << lib.fileName() << "with hints [" << static_cast<int>(loadHints) << "]";
  130. if (!lib.load())
  131. {
  132. qWarning() << "Pre-loading library" << lib.fileName() << "failed:" << lib.errorString() << "\nCheck your library search paths.";
  133. }
  134. }
  135. }
  136. ctkPluginFrameworkPrivate* const systemPluginPrivate = systemPlugin->d_func();
  137. systemPluginPrivate->initSystemPlugin();
  138. storage = new ctkPluginStorageSQL(this);
  139. dataStorage = ctkPluginFrameworkUtil::getFileStorage(this, "data");
  140. services = new ctkServices(this);
  141. plugins = new ctkPlugins(this);
  142. plugins->load();
  143. log() << "inited";
  144. initialized = true;
  145. log() << "Installed plugins:";
  146. // Use the ordering in the plugin storage to get a sorted list of plugins.
  147. QList<QSharedPointer<ctkPluginArchive> > allPAs = storage->getAllPluginArchives();
  148. foreach (QSharedPointer<ctkPluginArchive> pa, allPAs)
  149. {
  150. QSharedPointer<ctkPlugin> plugin = plugins->getPlugin(pa->getPluginLocation().toString());
  151. log() << " #" << plugin->getPluginId() << " " << plugin->getSymbolicName() << ":"
  152. << plugin->getVersion() << " location:" << plugin->getLocation();
  153. }
  154. }
  155. //----------------------------------------------------------------------------
  156. void ctkPluginFrameworkContext::uninit()
  157. {
  158. if (!initialized) return;
  159. log() << "uninit";
  160. ctkPluginFrameworkPrivate* const systemPluginPrivate = systemPlugin->d_func();
  161. systemPluginPrivate->uninitSystemPlugin();
  162. plugins->clear();
  163. delete plugins;
  164. plugins = 0;
  165. delete storage; // calls storage->close()
  166. storage = 0;
  167. delete services;
  168. services = 0;
  169. initialized = false;
  170. }
  171. //----------------------------------------------------------------------------
  172. int ctkPluginFrameworkContext::getId() const
  173. {
  174. return id;
  175. }
  176. //----------------------------------------------------------------------------
  177. QFileInfo ctkPluginFrameworkContext::getDataStorage(long id)
  178. {
  179. return QFileInfo(dataStorage.absolutePath() + '/' + QString::number(id) + '/');
  180. }
  181. //----------------------------------------------------------------------------
  182. void ctkPluginFrameworkContext::checkOurPlugin(ctkPlugin* plugin) const
  183. {
  184. ctkPluginPrivate* pp = plugin->d_func();
  185. if (this != pp->fwCtx)
  186. {
  187. throw ctkInvalidArgumentException("ctkPlugin does not belong to this framework: " + plugin->getSymbolicName());
  188. }
  189. }
  190. //----------------------------------------------------------------------------
  191. QDebug ctkPluginFrameworkContext::log() const
  192. {
  193. static QString nirvana;
  194. nirvana.clear();
  195. if (debug.framework)
  196. return qDebug() << "Framework instance " << getId() << ": ";
  197. else
  198. return QDebug(&nirvana);
  199. }
  200. //----------------------------------------------------------------------------
  201. void ctkPluginFrameworkContext::resolvePlugin(ctkPluginPrivate* plugin)
  202. {
  203. if (debug.resolve)
  204. {
  205. qDebug() << "resolve:" << plugin->symbolicName << "[" << plugin->id << "]";
  206. }
  207. // If we enter with tempResolved set, it means that we already have
  208. // resolved plugins. Check that it is true!
  209. if (tempResolved.size() > 0 && !tempResolved.contains(plugin))
  210. {
  211. ctkPluginException pe("resolve: InternalError1!", ctkPluginException::RESOLVE_ERROR);
  212. listeners.frameworkError(plugin->q_func(), pe);
  213. throw pe;
  214. }
  215. tempResolved.clear();
  216. tempResolved.insert(plugin);
  217. checkRequirePlugin(plugin);
  218. tempResolved.clear();
  219. if (debug.resolve)
  220. {
  221. qDebug() << "resolve: Done for" << plugin->symbolicName << "[" << plugin->id << "]";
  222. }
  223. }
  224. //----------------------------------------------------------------------------
  225. void ctkPluginFrameworkContext::checkRequirePlugin(ctkPluginPrivate *plugin)
  226. {
  227. if (!plugin->require.isEmpty())
  228. {
  229. if (debug.resolve)
  230. {
  231. qDebug() << "checkRequirePlugin: check requiring plugin" << plugin->id;
  232. }
  233. QListIterator<ctkRequirePlugin*> i(plugin->require);
  234. while (i.hasNext())
  235. {
  236. ctkRequirePlugin* pr = i.next();
  237. QList<ctkPlugin*> pl = plugins->getPlugins(pr->name, pr->pluginRange);
  238. ctkPluginPrivate* ok = 0;
  239. for (QListIterator<ctkPlugin*> pci(pl); pci.hasNext() && ok == 0; )
  240. {
  241. ctkPluginPrivate* p2 = pci.next()->d_func();
  242. if (tempResolved.contains(p2))
  243. {
  244. ok = p2;
  245. }
  246. else if (ctkPluginPrivate::RESOLVED_FLAGS & p2->state)
  247. {
  248. ok = p2;
  249. }
  250. else if (p2->state == ctkPlugin::INSTALLED) {
  251. QSet<ctkPluginPrivate*> oldTempResolved = tempResolved;
  252. tempResolved.insert(p2);
  253. // TODO check if operation locking is correct in case of
  254. // multi-threaded plug-in start up. Maybe refactor out the dependency
  255. // checking (use the "package" lock)
  256. ctkPluginPrivate::Locker sync(&p2->operationLock);
  257. p2->operation.fetchAndStoreOrdered(ctkPluginPrivate::RESOLVING);
  258. checkRequirePlugin(p2);
  259. tempResolved = oldTempResolved;
  260. p2->state = ctkPlugin::RESOLVED;
  261. listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::RESOLVED, p2->q_func()));
  262. p2->operation.fetchAndStoreOrdered(ctkPluginPrivate::IDLE);
  263. ok = p2;
  264. }
  265. }
  266. if (!ok && pr->resolution == ctkPluginConstants::RESOLUTION_MANDATORY)
  267. {
  268. tempResolved.clear();
  269. if (debug.resolve)
  270. {
  271. qDebug() << "checkRequirePlugin: failed to satisfy:" << pr->name;
  272. }
  273. throw ctkPluginException(QString("Failed to resolve required plugin: %1").arg(pr->name));
  274. }
  275. }
  276. }
  277. }
  278. //----------------------------------------------------------------------------
  279. void ctkPluginFrameworkContext::deleteFWDir()
  280. {
  281. QString d = ctkPluginFrameworkUtil::getFrameworkDir(this);
  282. QFileInfo fwDirInfo(d);
  283. if (fwDirInfo.exists())
  284. {
  285. if(fwDirInfo.isDir())
  286. {
  287. log() << "deleting old framework directory.";
  288. bool bOK = ctk::removeDirRecursively(fwDirInfo.absoluteFilePath());
  289. if(!bOK)
  290. {
  291. qDebug() << "Failed to remove existing fwdir" << fwDirInfo.absoluteFilePath();
  292. }
  293. }
  294. }
  295. }