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/iEnv>
29 #include <Common/iApp>
38 // Plugin manager interface implementation
39 static eVaf::Plugins::PluginManager
* mPluginManager
= 0;
41 } // namespace eVaf::Plugins::Internal
42 } // namespace eVaf::Plugins
46 //-------------------------------------------------------------------
49 using namespace eVaf::Plugins
;
51 PluginManager::PluginManager()
54 setObjectName(QString("%1-PluginManager").arg(VER_MODULE_NAME_STR
));
56 Internal::mPluginManager
= this;
58 d
= new Internal::PluginManagerPrivate
;
60 EVAF_INFO("%s created", qPrintable(objectName()));
63 PluginManager::~PluginManager()
67 Internal::mPluginManager
= 0;
69 EVAF_INFO("%s destroyed", qPrintable(objectName()));
72 PluginManager
* PluginManager::instance()
74 return Internal::mPluginManager
;
77 bool PluginManager::init()
79 // Initialize the internal implementation
83 EVAF_INFO("Loading plugins");
85 // Load and initialize plugins
86 if (!d
->loadPlugins())
89 EVAF_INFO("Plugins loaded");
93 EVAF_INFO("%s initialized", qPrintable(objectName()));
98 void PluginManager::done()
100 EVAF_INFO("Unloading plugins");
102 // Finalize and unload plugins
105 emit
pluginsUnloaded();
107 EVAF_INFO("Plugins unloaded");
109 // Finalize the internal implementation
112 EVAF_INFO("%s finalized", qPrintable(objectName()));
116 //-------------------------------------------------------------------
118 using namespace eVaf::Plugins::Internal
;
120 PluginManagerPrivate::PluginManagerPrivate()
123 setObjectName(QString("%1-PluginManagerPrivate").arg(VER_MODULE_NAME_STR
));
125 EVAF_INFO("%s created", qPrintable(objectName()));
128 PluginManagerPrivate::~PluginManagerPrivate()
130 EVAF_INFO("%s destroyed", qPrintable(objectName()));
133 bool PluginManagerPrivate::init()
135 EVAF_INFO("%s initialized", qPrintable(objectName()));
140 void PluginManagerPrivate::done()
142 EVAF_INFO("%s finalized", qPrintable(objectName()));
145 bool PluginManagerPrivate::loadPlugins()
147 // Get the name of the application's XML file
148 QString xmlFileName
= Common::iEnv::instance()->etcDir() + Common::iApp::instance()->xmlFileName();
151 QFile
xmlFile(xmlFileName
);
152 if (!xmlFile
.open(QFile::ReadOnly
)) {
153 EVAF_FATAL_ERROR("Failed to open '%s' : %s", qPrintable(xmlFileName
), qPrintable(xmlFile
.errorString()));
157 // Process the XML file
158 QXmlStreamReader
xml(&xmlFile
);
159 bool isValid
= false;
160 bool isPlugins
= false;
161 bool isQtPlugins
= false;
162 bool isPlugin
= false;
166 QStringList qtPlugins
;
168 while (!xml
.atEnd()) {
172 if (xml
.isStartElement()) {
174 // Not a valid XML file yet?
176 if (xml
.name() == "eVaf") {
180 EVAF_FATAL_ERROR("'%s' is not a valid XML file for eVaf applications", qPrintable(xmlFileName
));
185 // This is a valid XML file
188 // No plugins or qtplugins sections yet?
189 if (!isPlugins
&& !isQtPlugins
) {
190 if (xml
.name() == "plugins") {
191 // Check for windows and linux only sections
193 if (Common::isTrue(xml
.attributes().value("windowsonly").toString()))
197 if (Common::isTrue(xml
.attributes().value("linuxonly").toString()))
203 else if (xml
.name() == "qtplugins") {
204 // Check for windows and linux only sections
206 if (Common::isTrue(xml
.attributes().value("windowsonly").toString()))
210 if (Common::isTrue(xml
.attributes().value("linuxonly").toString()))
218 // An individual plugin?
219 else if (isPlugins
&& xml
.name() == "plugin") {
220 // Check for windows and linux only plugins
222 if (Common::isTrue(xml
.attributes().value("windowsonly").toString())) {
223 EVAF_INFO("Plugin '%s' is for Windows only", qPrintable(xml
.attributes().value("name").toString()));
228 if (Common::isTrue(xml
.attributes().value("linuxonly").toString())) {
229 EVAF_INFO("Plugin '%s' is for Linux only", qPrintable(xml
.attributes().value("name").toString()));
234 pluginName
= xml
.attributes().value("name").toString();
235 moduleName
= xml
.attributes().value("filename").toString();
237 /// @TODO: If the file name attribute is empty, loog for the config attribute
238 if (moduleName
.isEmpty())
247 args
.append("<" + xml
.name().toString());
248 for (int i
= 0; i
< xml
.attributes().size(); ++i
)
249 args
.append(" " + xml
.attributes().at(i
).name().toString() + "=\"" + xml
.attributes().at(i
).value().toString() + "\"");
253 // An individual Qt plugin?
254 else if (isQtPlugins
&& xml
.name() == "plugin") {
255 // Check for windows and linux only plugins
257 if (Common::isTrue(xml
.attributes().value("windowsonly").toString())) {
258 EVAF_INFO("Qt plugin '%s' is for Windows only", qPrintable(xml
.attributes().value("name").toString()));
263 if (Common::isTrue(xml
.attributes().value("linuxonly").toString())) {
264 EVAF_INFO("Qt plugin '%s' is for Linux only", qPrintable(xml
.attributes().value("name").toString()));
268 QString name
= xml
.attributes().value("filename").toString();
269 if (!name
.isEmpty() && !qtPlugins
.contains(name
))
270 qtPlugins
.append(name
);
276 else if (xml
.isEndElement()) {
277 if (isPlugin
&& xml
.name() == "plugin") {
279 Module
* m
= moduleByName(moduleName
);
281 mModules
.append(QExplicitlySharedDataPointer
<Module
>(m
= new Module(moduleName
)));
282 mPlugins
.append(QExplicitlySharedDataPointer
<Plugin
>(new Plugin(m
, pluginName
, args
)));
285 args
.append("</" + xml
.name().toString() + ">");
286 else if (xml
.name() == "plugins")
288 else if (xml
.name() == "qtplugins")
290 else if (xml
.name() == "eVaf")
297 for (i
= 0; i
< qtPlugins
.size(); ++i
) {
298 loadQtPlugin(qtPlugins
.at(i
));
303 while (i
< mPlugins
.size()) {
304 if (!mPlugins
.at(i
)->load()) {
305 EVAF_ERROR("Failed to load module '%s'", qPrintable(mPlugins
.at(i
)->name()));
306 mPlugins
.removeAt(i
);
312 // Initialize eVaf plugins
314 while (i
< mPlugins
.size()) {
315 if (!mPlugins
.at(i
)->init()) {
316 EVAF_ERROR("Failed to initialize module '%s'", qPrintable(mPlugins
.at(i
)->name()));
317 mPlugins
.removeAt(i
);
326 void PluginManagerPrivate::unloadPlugins()
328 // Finalize all the plugins
329 for (int i
= 0; i
< mPlugins
.size(); ++i
)
330 mPlugins
.at(i
)->done();
331 while (!mPlugins
.isEmpty()) {
332 QExplicitlySharedDataPointer
<Plugin
> p
= mPlugins
.takeLast();
336 // Unload all the modules
337 while (!mModules
.isEmpty()) {
338 QExplicitlySharedDataPointer
<Module
> m
= mModules
.takeLast();
343 Module
* PluginManagerPrivate::moduleByName(QString
const & name
) const
345 for (int i
= 0; i
< mModules
.size(); ++i
) {
346 if (mModules
.at(i
)->name() == name
)
347 return mModules
.at(i
).data();
352 bool PluginManagerPrivate::loadQtPlugin(QString
const & name
) const
354 // Get the Qt plugin file name with the full path
358 fileName
= QString("%1libq%2.so").arg(Common::iEnv::instance()->qtPluginsDir()).arg(name
);
360 QString t
= QString("%1libq%2.so.debug").arg(Common::iEnv::instance()->qtPluginsDir()).arg(name
);
361 if (QFile::exists(t
))
367 fileName
= QString("%2q%2%3").arg(Common::iEnv::instance()->qtPluginsDir()).arg(name
).arg("4.dll");
369 QString t
= QString("%1q%2%3").arg(Common::iEnv::instance()->qtPluginsDir()).arg(name
).arg("d4.dll");
370 if (!QFile::exists(t
))
375 if (fileName
.isEmpty()) {
376 EVAF_ERROR("Don\'t know how to load Qt plugin '%s'", qPrintable(name
));
380 EVAF_INFO("Loading Qt plugin '%s'", qPrintable(fileName
));
382 QLibrary
lib(fileName
);
383 void * fn
= lib
.resolve("qt_plugin_instance");
385 qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunction(fn
));
389 EVAF_ERROR("Failed to load Qt plugin '%s' : %s", qPrintable(fileName
), qPrintable(lib
.errorString()));
395 //-------------------------------------------------------------------
397 Module::Module(QString
const & name
)
416 // The real file name with path
417 QString fileName
= Common::iEnv::instance()->binDir() + expandPluginName(mName
);
419 // Try to load the module
420 QScopedPointer
<QPluginLoader
> p(new QPluginLoader(fileName
));
422 EVAF_FATAL_ERROR("Failed to load '%s' : %s", qPrintable(mName
), qPrintable(p
->errorString()));
426 // Get the root component
427 QObject
* root
= p
->instance();
429 // Does the module implement the iPluginFactory interface?
430 if ((mPluginFactory
= qobject_cast
<iPluginFactory
*>(root
)) == 0) {
432 // If not, then it has to implement the iPlugin interface
433 if (qobject_cast
<iPlugin
*>(root
) == 0) {
434 EVAF_FATAL_ERROR("Module '%s' is not a valid eVaf module", qPrintable(mName
));
444 void Module::unload()
456 iPlugin
* Module::create(QString
const & name
)
458 // If the module is not loaded, load it now
466 // Does the module implement the iPluginFactory interface?
467 if (mPluginFactory
) {
468 // Use the iPluginFactory interface to create the requested interface
469 i
= qobject_cast
<iPlugin
*>(mPluginFactory
->create(name
));
471 EVAF_FATAL_ERROR("Module '%s' failed to create the iPlugin interface with name '%s'", qPrintable(mName
), qPrintable(name
));
476 // Otherwise use the root component, but only once
479 EVAF_FATAL_ERROR("Module '%s' can implement only one iPlugin interface and one with the name '%s' is already created",
480 qPrintable(mName
), qPrintable(mPlugin
->objectName()));
484 i
= qobject_cast
<iPlugin
*>(mRoot
);
486 EVAF_FATAL_ERROR("Module '%s' does not implement the iPlugin interface", qPrintable(mName
));
497 //-------------------------------------------------------------------
499 Plugin::Plugin(Module
* module, QString
const & name
, QString
const & args
)
513 mPlugin
= mModule
->create(mName
);
514 if (mPlugin
&& !mPlugin
->objectName().isEmpty())
515 mName
= mPlugin
->objectName();
519 void Plugin::unload()
528 return mPlugin
->init(mArgs
);