2 * @file plugins/pluginmanager.cpp
3 * @brief Implementation of the plugin manager
5 * Copyright (c) 2011 Enar Vaikene
7 * This file is part of the eVaf C++ cross-platform application development framework.
9 * This file can be used under the terms of the GNU General Public License
10 * version 3.0 as published by the Free Software Foundation and appearing in
11 * the file LICENSE included in the packaging of this file. Please review the
12 * the following information to ensure the GNU General Public License version
13 * 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
15 * Alternatively, this file may be used in accordance with the Commercial License
16 * Agreement provided with the Software.
19 #include "pluginmanager.h"
20 #include "pluginmanager_p.h"
22 #include "ipluginfactory.h"
25 #include <Common/Globals>
26 #include <Common/Util>
27 #include <Common/iLogger>
28 #include <Common/iApp>
37 // Plugin manager interface implementation
38 static eVaf::Plugins::PluginManager
* mPluginManager
= nullptr;
40 } // namespace eVaf::Plugins::Internal
41 } // namespace eVaf::Plugins
45 //-------------------------------------------------------------------
48 using namespace eVaf::Plugins
;
50 PluginManager::PluginManager()
52 , d(new Internal::PluginManagerPrivate
)
54 setObjectName(QString("%1-PluginManager").arg(VER_MODULE_NAME_STR
));
56 Internal::mPluginManager
= this;
58 EVAF_INFO("%s created", qPrintable(objectName()));
61 PluginManager::~PluginManager()
65 Internal::mPluginManager
= nullptr;
67 EVAF_INFO("%s destroyed", qPrintable(objectName()));
70 PluginManager
* PluginManager::instance()
72 return Internal::mPluginManager
;
75 bool PluginManager::init()
77 // Initialize the internal implementation
81 EVAF_INFO("Loading plugins");
83 // Load and initialize plugins
84 if (!d
->loadPlugins())
87 EVAF_INFO("Plugins loaded");
91 EVAF_INFO("%s initialized", qPrintable(objectName()));
96 void PluginManager::done()
98 EVAF_INFO("Unloading plugins");
100 // Finalize and unload plugins
103 emit
pluginsUnloaded();
105 EVAF_INFO("Plugins unloaded");
107 // Finalize the internal implementation
110 EVAF_INFO("%s finalized", qPrintable(objectName()));
114 //-------------------------------------------------------------------
116 using namespace eVaf::Plugins::Internal
;
118 PluginManagerPrivate::PluginManagerPrivate()
121 setObjectName(QString("%1-PluginManagerPrivate").arg(VER_MODULE_NAME_STR
));
123 EVAF_INFO("%s created", qPrintable(objectName()));
126 PluginManagerPrivate::~PluginManagerPrivate()
128 EVAF_INFO("%s destroyed", qPrintable(objectName()));
131 bool PluginManagerPrivate::init()
133 EVAF_INFO("%s initialized", qPrintable(objectName()));
138 void PluginManagerPrivate::done()
140 EVAF_INFO("%s finalized", qPrintable(objectName()));
143 bool PluginManagerPrivate::loadPlugins()
145 // Get the name of the application's XML file
146 QString xmlFileName
= Common::iApp::instance()->etcDir() + Common::iApp::instance()->xmlFileName();
149 QFile
xmlFile(xmlFileName
);
150 if (!xmlFile
.open(QFile::ReadOnly
)) {
151 EVAF_FATAL_ERROR("Failed to open '%s' : %s", qPrintable(xmlFileName
), qPrintable(xmlFile
.errorString()));
155 // Process the XML file
156 QXmlStreamReader
xml(&xmlFile
);
157 bool isValid
= false;
158 bool isPlugins
= false;
159 bool isQtPlugins
= false;
160 bool isPlugin
= false;
164 QStringList qtPlugins
;
166 while (!xml
.atEnd()) {
170 if (xml
.isStartElement()) {
172 // Not a valid XML file yet?
174 if (xml
.name() == "eVaf") {
178 EVAF_FATAL_ERROR("'%s' is not a valid XML file for eVaf applications", qPrintable(xmlFileName
));
183 // This is a valid XML file
186 // No plugins or qtplugins sections yet?
187 if (!isPlugins
&& !isQtPlugins
) {
188 if (xml
.name() == "plugins") {
189 // Check for windows and linux only sections
190 #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
191 if (Common::isTrue(xml
.attributes().value("windowsonly").toString()))
195 if (Common::isTrue(xml
.attributes().value("linuxonly").toString()))
201 else if (xml
.name() == "qtplugins") {
202 // Check for windows and linux only sections
203 #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
204 if (Common::isTrue(xml
.attributes().value("windowsonly").toString()))
208 if (Common::isTrue(xml
.attributes().value("linuxonly").toString()))
216 // An individual plugin?
217 else if (isPlugins
&& xml
.name() == "plugin") {
218 // Check for windows and linux only plugins
219 #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
220 if (Common::isTrue(xml
.attributes().value("windowsonly").toString())) {
221 EVAF_INFO("Plugin '%s' is for Windows only", qPrintable(xml
.attributes().value("name").toString()));
226 if (Common::isTrue(xml
.attributes().value("linuxonly").toString())) {
227 EVAF_INFO("Plugin '%s' is for Linux only", qPrintable(xml
.attributes().value("name").toString()));
232 pluginName
= xml
.attributes().value("name").toString();
233 moduleName
= xml
.attributes().value("filename").toString();
235 /// @TODO: If the file name attribute is empty, loog for the config attribute
236 if (moduleName
.isEmpty())
245 args
.append("<" + xml
.name().toString());
246 for (int i
= 0; i
< xml
.attributes().size(); ++i
)
247 args
.append(" " + xml
.attributes().at(i
).name().toString() + "=\"" + xml
.attributes().at(i
).value().toString() + "\"");
251 // An individual Qt plugin?
252 else if (isQtPlugins
&& xml
.name() == "plugin") {
253 // Check for windows and linux only plugins
254 #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
255 if (Common::isTrue(xml
.attributes().value("windowsonly").toString())) {
256 EVAF_INFO("Qt plugin '%s' is for Windows only", qPrintable(xml
.attributes().value("name").toString()));
261 if (Common::isTrue(xml
.attributes().value("linuxonly").toString())) {
262 EVAF_INFO("Qt plugin '%s' is for Linux only", qPrintable(xml
.attributes().value("name").toString()));
266 QString name
= xml
.attributes().value("filename").toString();
267 if (!name
.isEmpty() && !qtPlugins
.contains(name
))
268 qtPlugins
.append(name
);
274 else if (xml
.isEndElement()) {
275 if (isPlugin
&& xml
.name() == "plugin") {
277 Module
* m
= moduleByName(moduleName
);
279 mModules
.append(QExplicitlySharedDataPointer
<Module
>(m
= new Module(moduleName
)));
280 mPlugins
.append(QExplicitlySharedDataPointer
<Plugin
>(new Plugin(m
, pluginName
, args
)));
283 args
.append("</" + xml
.name().toString() + ">");
284 else if (xml
.name() == "plugins")
286 else if (xml
.name() == "qtplugins")
288 else if (xml
.name() == "eVaf")
295 while (i
< mPlugins
.size()) {
296 if (!mPlugins
.at(i
)->load()) {
297 EVAF_ERROR("Failed to load module '%s'", qPrintable(mPlugins
.at(i
)->name()));
298 mPlugins
.removeAt(i
);
304 // Initialize eVaf plugins
306 while (i
< mPlugins
.size()) {
307 if (!mPlugins
.at(i
)->init()) {
308 EVAF_ERROR("Failed to initialize module '%s'", qPrintable(mPlugins
.at(i
)->name()));
309 mPlugins
.removeAt(i
);
318 void PluginManagerPrivate::unloadPlugins()
320 // Finalize all the plugins
321 for (int i
= mPlugins
.size() - 1; i
>= 0; --i
)
322 mPlugins
.at(i
)->done();
323 while (!mPlugins
.isEmpty()) {
324 QExplicitlySharedDataPointer
<Plugin
> p
= mPlugins
.takeLast();
328 // Unload all the modules
329 while (!mModules
.isEmpty()) {
330 QExplicitlySharedDataPointer
<Module
> m
= mModules
.takeLast();
335 Module
* PluginManagerPrivate::moduleByName(QString
const & name
) const
337 for (int i
= 0; i
< mModules
.size(); ++i
) {
338 if (mModules
.at(i
)->name() == name
)
339 return mModules
.at(i
).data();
345 //-------------------------------------------------------------------
347 Module::Module(QString
const & name
)
352 , mPluginFactory(nullptr)
365 // The real file name with path
366 QString fileName
= Common::iApp::instance()->binDir() + expandPluginName(mName
);
368 // Try to load the module
369 QScopedPointer
<QPluginLoader
> p(new QPluginLoader(fileName
));
371 EVAF_FATAL_ERROR("Failed to load '%s' : %s", qPrintable(mName
), qPrintable(p
->errorString()));
375 // Get the root component
376 QObject
* root
= p
->instance();
378 // Does the module implement the iPluginFactory interface?
379 if ((mPluginFactory
= qobject_cast
<iPluginFactory
*>(root
)) == nullptr) {
381 // If not, then it has to implement the iPlugin interface
382 if (qobject_cast
<iPlugin
*>(root
) == nullptr) {
383 EVAF_FATAL_ERROR("Module '%s' is not a valid eVaf module", qPrintable(mName
));
389 mLoader
.reset(p
.take());
393 void Module::unload()
396 mPluginFactory
= nullptr;
404 iPlugin
* Module::create(QString
const & name
)
406 // If the module is not loaded, load it now
412 iPlugin
* i
= nullptr;
414 // Does the module implement the iPluginFactory interface?
415 if (mPluginFactory
) {
416 // Use the iPluginFactory interface to create the requested interface
417 i
= qobject_cast
<iPlugin
*>(mPluginFactory
->create(name
));
419 EVAF_FATAL_ERROR("Module '%s' failed to create the iPlugin interface with name '%s'", qPrintable(mName
), qPrintable(name
));
424 // Otherwise use the root component, but only once
427 EVAF_FATAL_ERROR("Module '%s' can implement only one iPlugin interface and one with the name '%s' is already created",
428 qPrintable(mName
), qPrintable(mPlugin
->objectName()));
432 i
= qobject_cast
<iPlugin
*>(mRoot
);
434 EVAF_FATAL_ERROR("Module '%s' does not implement the iPlugin interface", qPrintable(mName
));
445 //-------------------------------------------------------------------
447 Plugin::Plugin(Module
* module, QString
const & name
, QString
const & args
)
461 mPlugin
= mModule
->create(mName
);
462 if (mPlugin
&& !mPlugin
->objectName().isEmpty())
463 mName
= mPlugin
->objectName();
464 return mPlugin
!= nullptr;
467 void Plugin::unload()
476 return mPlugin
->init(mArgs
);