From 2c55fe2306e2efe23417b69cecfbbd2835f859f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Garapich?= Date: Mon, 8 Jun 2015 01:30:46 +0200 Subject: [PATCH] refs #442 Add CPluginManager class --- src/blackcore/context_simulator_impl.cpp | 58 ++++------ src/blackcore/context_simulator_impl.h | 6 +- src/blackcore/plugin_manager.cpp | 131 +++++++++++++++++++++++ src/blackcore/plugin_manager.h | 65 +++++++++++ 4 files changed, 217 insertions(+), 43 deletions(-) create mode 100644 src/blackcore/plugin_manager.cpp create mode 100644 src/blackcore/plugin_manager.h diff --git a/src/blackcore/context_simulator_impl.cpp b/src/blackcore/context_simulator_impl.cpp index 01ffd5cc9..b218f62da 100644 --- a/src/blackcore/context_simulator_impl.cpp +++ b/src/blackcore/context_simulator_impl.cpp @@ -13,6 +13,7 @@ #include "context_settings.h" #include "context_application.h" #include "context_network_impl.h" +#include "plugin_manager.h" #include "context_runtime.h" #include "blackcore/blackcorefreefunctions.h" #include "blackmisc/propertyindexvariantmap.h" @@ -53,21 +54,12 @@ namespace BlackCore if (!plugin->factory) { - QPluginLoader loader(plugin->fileName); - QObject *instance = loader.instance(); - if (instance) + CPluginManager *pm = CPluginManager::getInstance(); + ISimulatorFactory *factory = qobject_cast(pm->getPluginById(plugin->identifier)); + if (factory) { - ISimulatorFactory *factory = qobject_cast(instance); - if (factory) - { - plugin->factory = factory; - CLogMessage(this).info("Loaded driver: %1") << plugin->info.toQString(); - } - } - else - { - QString errorMsg = loader.errorString().append(" ").append("Also check if required dll/libs of plugin exists"); - CLogMessage(this).error(errorMsg); + plugin->factory = factory; + CLogMessage(this).info("Loaded driver: %1") << plugin->info.toQString(); } } @@ -692,39 +684,25 @@ namespace BlackCore void CContextSimulator::findSimulatorPlugins() { - const QString path = qApp->applicationDirPath().append("/plugins/simulator"); - m_pluginsDir = QDir(path); - if (!m_pluginsDir.exists()) - { - CLogMessage(this).error("No plugin directory: %1") << m_pluginsDir.currentPath(); - return; - } + CPluginManager *pm = CPluginManager::getInstance(); + auto plugins = pm->plugins("org.swift-project.blackcore.simulatorinterface"); - QStringList fileNames = m_pluginsDir.entryList(QDir::Files); - fileNames.sort(Qt::CaseInsensitive); // give a certain order, rather than random file order - for (const auto &fileName : fileNames) + std::for_each(plugins.begin(), plugins.end(), [this](const QJsonObject &json) { - if (!QLibrary::isLibrary(fileName)) + QString identifier = json.value("MetaData").toObject().value("identifier").toString(); + Q_ASSERT(!identifier.isEmpty()); + CSimulatorPluginInfo info; + info.convertFromJson(json); + if (info.isValid()) { - continue; - } - - CLogMessage(this).debug() << "Try to load plugin: " << fileName; - QString pluginPath = m_pluginsDir.absoluteFilePath(fileName); - QPluginLoader loader(pluginPath); - QJsonObject json = loader.metaData(); - CSimulatorPluginInfo simulatorPluginInfo; - simulatorPluginInfo.convertFromJson(json); - if (simulatorPluginInfo.isValid()) - { - m_simulatorPlugins << PluginData { simulatorPluginInfo, nullptr, nullptr, nullptr, pluginPath}; - CLogMessage(this).debug() << "Found simulator driver: " << simulatorPluginInfo.toQString(); + m_simulatorPlugins << PluginData { info, nullptr, nullptr, nullptr, identifier }; + CLogMessage(this).debug() << "Found simulator driver: " << info.toQString(); } else { - CLogMessage(this).warning("Simulator driver in %1 is invalid") << pluginPath; + CLogMessage(this).warning("Simulator driver in %1 is invalid") << identifier; } - } + }); } void CContextSimulator::stopSimulatorListeners() diff --git a/src/blackcore/context_simulator_impl.h b/src/blackcore/context_simulator_impl.h index 07a726572..12b933000 100644 --- a/src/blackcore/context_simulator_impl.h +++ b/src/blackcore/context_simulator_impl.h @@ -182,14 +182,14 @@ namespace BlackCore //! \todo Would we want to use m_member style here? struct PluginData { - PluginData(const BlackMisc::Simulation::CSimulatorPluginInfo &info, ISimulatorFactory *factory, ISimulatorListener *listener, ISimulator *simulator, const QString &fileName) : - info(info), factory(factory), listener(listener), simulator(simulator), fileName(fileName) {} + PluginData(const BlackMisc::Simulation::CSimulatorPluginInfo &info, ISimulatorFactory *factory, ISimulatorListener *listener, ISimulator *simulator, const QString &identifier) : + info(info), factory(factory), listener(listener), simulator(simulator), identifier(identifier) {} BlackMisc::Simulation::CSimulatorPluginInfo info; ISimulatorFactory *factory = nullptr; //!< Lazy-loaded, nullptr by default ISimulatorListener *listener = nullptr; //!< Listener instance, nullptr by default ISimulator *simulator = nullptr; //!< The simulator itself (always nullptr unless it is the currently working one) - QString fileName; //!< Plugin file name (relative to plugins/simulator) + QString identifier = QString(); //!< The plugin identifier QHash m_storage; //!< Permanent plugin storage - data stored here will be kept even when plugin is unloaded }; diff --git a/src/blackcore/plugin_manager.cpp b/src/blackcore/plugin_manager.cpp new file mode 100644 index 000000000..82e59dba8 --- /dev/null +++ b/src/blackcore/plugin_manager.cpp @@ -0,0 +1,131 @@ +/* Copyright (C) 2013 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +#include "plugin_manager.h" +#include "blackmisc/logmessage.h" +#include "blackmisc/loghandler.h" +#include +#include +#include +#include + +using namespace BlackMisc; + +namespace BlackCore { + + BlackMisc::CSequence CPluginManager::plugins(const QString &iid) const + { + return m_metadatas.values(iid); + } + + QObject *CPluginManager::getPluginById(const QString &identifier) + { + if (m_instances.contains(identifier)) + { + return m_instances.value(identifier); + } + + if (!m_paths.contains(identifier)) + { + CLogMessage(this).warning("Plugin with id %1 does not exist") << identifier; + return nullptr; + } + + QString path = m_paths.value(identifier); + QPluginLoader loader(path); + QObject *instance = loader.instance(); + if (instance) + { + m_instances.insert(identifier, instance); + return instance; + } + else + { + CLogMessage(this).error(loader.errorString()); + return nullptr; + } + } + + CPluginManager *CPluginManager::getInstance() + { + static CPluginManager *instance = nullptr; + if (!instance) + { + instance = new CPluginManager(); + } + return instance; + } + + CPluginManager::CPluginManager(QObject *parent) : QObject(parent) + { + collectPlugins(); + + connect(qApp, &QCoreApplication::aboutToQuit, this, &CPluginManager::deleteLater); + } + + void CPluginManager::collectPlugins() + { + QDir pluginDir(qApp->applicationDirPath().append("/plugins")); + if (!pluginDir.exists()) + { + CLogMessage(this).warning("No plugins directory: %1") << pluginDir.path(); + return; + } + + QDirIterator it(pluginDir, QDirIterator::Subdirectories); + while (it.hasNext()) + { + if (!QLibrary::isLibrary(it.next())) + { + continue; + } + + CLogMessage(this).debug() << "Loading plugin: " << it.filePath(); + QPluginLoader loader(it.filePath()); + QJsonObject json = loader.metaData(); + QString identifier = pluginIdentifier(json); + if (identifier.isEmpty()) + { + CLogMessage(this).warning("Plugin %1 invalid, not loading it") << it.filePath(); + continue; + } + + m_paths.insert(identifier, it.filePath()); + m_metadatas.insert(json["IID"].toString(), json); + } + } + + QString CPluginManager::pluginIdentifier(const QJsonObject &metadata) + { + if (!metadata.contains("IID") || !metadata["IID"].isString()) + { + return QString(); + } + + if (!metadata["MetaData"].isObject()) + { + return QString(); + } + + QJsonObject data = metadata["MetaData"].toObject(); + if (!data.contains("identifier")) + { + return QString(); + } + + QJsonValue identifier = data["identifier"]; + if (!identifier.isString()) + { + return QString(); + } + + return identifier.toString(); + } + +} // namespace diff --git a/src/blackcore/plugin_manager.h b/src/blackcore/plugin_manager.h new file mode 100644 index 000000000..3cfe73064 --- /dev/null +++ b/src/blackcore/plugin_manager.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2013 + * swift Project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, + * including this file, may be copied, modified, propagated, or distributed except according to the terms + * contained in the LICENSE file. + */ + +//! \file + +#ifndef CPLUGINMANAGER_H +#define CPLUGINMANAGER_H + +#include "blackmisc/sequence.h" +#include +#include +#include + +namespace BlackCore +{ + + /*! + * \brief Manages all installed plugins, provides easy access to all of them. + * Plugin loading works as follows: + * 1. Collect all interesting plugins by their IID (\ref plugins()); + * all plugins are guaranteed to be valid, i.e. they have valid IID and identifier. + * 2. Load specified plugin (\ref getPluginById()). + */ + class CPluginManager : public QObject + { + Q_OBJECT + + public: + //! Retrieves plugin metadata list for the given IID. + //! If no implementations are found, the empty list is returned. + BlackMisc::CSequence plugins(const QString &iid) const; + + //! Loads the given plugin (if necessary) and returns its instance. + //! Returns _nullptr_ on failure. + QObject *getPluginById(const QString &identifier); + + //! Gets the singleton + static CPluginManager *getInstance(); + + protected: + //! Constructor + explicit CPluginManager(QObject *parent = nullptr); + + private: + //! Looks (recursively) for all installed plugins + void collectPlugins(); + + //! Checks whether the provided metadata is valid + //! \return The plugin identifier + QString pluginIdentifier(const QJsonObject &metadata); + + QMultiMap m_metadatas; //!< IID <-> metadata pairs + QMap m_paths; //!< identifier <-> file path pairs + QMap m_instances; //!< identifier <-> instance pairs + + }; +} + +#endif // CPLUGINMANAGER_H