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
= 0;
40 } // namespace eVaf::Plugins::Internal
41 } // namespace eVaf::Plugins
45 //-------------------------------------------------------------------
48 using namespace eVaf::Plugins
;
50 PluginManager::PluginManager()
53 setObjectName(QString("%1-PluginManager").arg(VER_MODULE_NAME_STR
));
55 Internal::mPluginManager
= this;
57 d
= new Internal::PluginManagerPrivate
;
59 EVAF_INFO("%s created", qPrintable(objectName()));
62 PluginManager::~PluginManager()
66 Internal::mPluginManager
= 0;
68 EVAF_INFO("%s destroyed", qPrintable(objectName()));
71 PluginManager
* PluginManager::instance()
73 return Internal::mPluginManager
;
76 bool PluginManager::init()
78 // Initialize the internal implementation
82 EVAF_INFO("Loading plugins");
84 // Load and initialize plugins
85 if (!d
->loadPlugins())
88 EVAF_INFO("Plugins loaded");
92 EVAF_INFO("%s initialized", qPrintable(objectName()));
97 void PluginManager::done()
99 EVAF_INFO("Unloading plugins");
101 // Finalize and unload plugins
104 emit
pluginsUnloaded();
106 EVAF_INFO("Plugins unloaded");
108 // Finalize the internal implementation
111 EVAF_INFO("%s finalized", qPrintable(objectName()));
115 //-------------------------------------------------------------------
117 using namespace eVaf::Plugins::Internal
;
119 PluginManagerPrivate::PluginManagerPrivate()
122 setObjectName(QString("%1-PluginManagerPrivate").arg(VER_MODULE_NAME_STR
));
124 EVAF_INFO("%s created", qPrintable(objectName()));
127 PluginManagerPrivate::~PluginManagerPrivate()
129 EVAF_INFO("%s destroyed", qPrintable(objectName()));
132 bool PluginManagerPrivate::init()
134 EVAF_INFO("%s initialized", qPrintable(objectName()));
139 void PluginManagerPrivate::done()
141 EVAF_INFO("%s finalized", qPrintable(objectName()));
144 bool PluginManagerPrivate::loadPlugins()
146 // Get the name of the application's XML file
147 QString xmlFileName
= Common::iApp::instance()->etcDir() + Common::iApp::instance()->xmlFileName();
150 QFile
xmlFile(xmlFileName
);
151 if (!xmlFile
.open(QFile::ReadOnly
)) {
152 EVAF_FATAL_ERROR("Failed to open '%s' : %s", qPrintable(xmlFileName
), qPrintable(xmlFile
.errorString()));
156 // Process the XML file
157 QXmlStreamReader
xml(&xmlFile
);
158 bool isValid
= false;
159 bool isPlugins
= false;
160 bool isQtPlugins
= false;
161 bool isPlugin
= false;
165 QStringList qtPlugins
;
167 while (!xml
.atEnd()) {
171 if (xml
.isStartElement()) {
173 // Not a valid XML file yet?
175 if (xml
.name() == "eVaf") {
179 EVAF_FATAL_ERROR("'%s' is not a valid XML file for eVaf applications", qPrintable(xmlFileName
));
184 // This is a valid XML file
187 // No plugins or qtplugins sections yet?
188 if (!isPlugins
&& !isQtPlugins
) {
189 if (xml
.name() == "plugins") {
190 // Check for windows and linux only sections
192 if (Common::isTrue(xml
.attributes().value("windowsonly").toString()))
196 if (Common::isTrue(xml
.attributes().value("linuxonly").toString()))
202 else if (xml
.name() == "qtplugins") {
203 // Check for windows and linux only sections
205 if (Common::isTrue(xml
.attributes().value("windowsonly").toString()))
209 if (Common::isTrue(xml
.attributes().value("linuxonly").toString()))
217 // An individual plugin?
218 else if (isPlugins
&& xml
.name() == "plugin") {
219 // Check for windows and linux only plugins
221 if (Common::isTrue(xml
.attributes().value("windowsonly").toString())) {
222 EVAF_INFO("Plugin '%s' is for Windows only", qPrintable(xml
.attributes().value("name").toString()));
227 if (Common::isTrue(xml
.attributes().value("linuxonly").toString())) {
228 EVAF_INFO("Plugin '%s' is for Linux only", qPrintable(xml
.attributes().value("name").toString()));
233 pluginName
= xml
.attributes().value("name").toString();
234 moduleName
= xml
.attributes().value("filename").toString();
236 /// @TODO: If the file name attribute is empty, loog for the config attribute
237 if (moduleName
.isEmpty())
246 args
.append("<" + xml
.name().toString());
247 for (int i
= 0; i
< xml
.attributes().size(); ++i
)
248 args
.append(" " + xml
.attributes().at(i
).name().toString() + "=\"" + xml
.attributes().at(i
).value().toString() + "\"");
252 // An individual Qt plugin?
253 else if (isQtPlugins
&& xml
.name() == "plugin") {
254 // Check for windows and linux only plugins
256 if (Common::isTrue(xml
.attributes().value("windowsonly").toString())) {
257 EVAF_INFO("Qt plugin '%s' is for Windows only", qPrintable(xml
.attributes().value("name").toString()));
262 if (Common::isTrue(xml
.attributes().value("linuxonly").toString())) {
263 EVAF_INFO("Qt plugin '%s' is for Linux only", qPrintable(xml
.attributes().value("name").toString()));
267 QString name
= xml
.attributes().value("filename").toString();
268 if (!name
.isEmpty() && !qtPlugins
.contains(name
))
269 qtPlugins
.append(name
);
275 else if (xml
.isEndElement()) {
276 if (isPlugin
&& xml
.name() == "plugin") {
278 Module
* m
= moduleByName(moduleName
);
280 mModules
.append(QExplicitlySharedDataPointer
<Module
>(m
= new Module(moduleName
)));
281 mPlugins
.append(QExplicitlySharedDataPointer
<Plugin
>(new Plugin(m
, pluginName
, args
)));
284 args
.append("</" + xml
.name().toString() + ">");
285 else if (xml
.name() == "plugins")
287 else if (xml
.name() == "qtplugins")
289 else if (xml
.name() == "eVaf")
296 while (i
< mPlugins
.size()) {
297 if (!mPlugins
.at(i
)->load()) {
298 EVAF_ERROR("Failed to load module '%s'", qPrintable(mPlugins
.at(i
)->name()));
299 mPlugins
.removeAt(i
);
305 // Initialize eVaf plugins
307 while (i
< mPlugins
.size()) {
308 if (!mPlugins
.at(i
)->init()) {
309 EVAF_ERROR("Failed to initialize module '%s'", qPrintable(mPlugins
.at(i
)->name()));
310 mPlugins
.removeAt(i
);
319 void PluginManagerPrivate::unloadPlugins()
321 // Finalize all the plugins
322 for (int i
= mPlugins
.size() - 1; i
>= 0; --i
)
323 mPlugins
.at(i
)->done();
324 while (!mPlugins
.isEmpty()) {
325 QExplicitlySharedDataPointer
<Plugin
> p
= mPlugins
.takeLast();
329 // Unload all the modules
330 while (!mModules
.isEmpty()) {
331 QExplicitlySharedDataPointer
<Module
> m
= mModules
.takeLast();
336 Module
* PluginManagerPrivate::moduleByName(QString
const & name
) const
338 for (int i
= 0; i
< mModules
.size(); ++i
) {
339 if (mModules
.at(i
)->name() == name
)
340 return mModules
.at(i
).data();
346 //-------------------------------------------------------------------
348 Module::Module(QString
const & name
)
367 // The real file name with path
368 QString fileName
= Common::iApp::instance()->binDir() + expandPluginName(mName
);
370 // Try to load the module
371 QScopedPointer
<QPluginLoader
> p(new QPluginLoader(fileName
));
373 EVAF_FATAL_ERROR("Failed to load '%s' : %s", qPrintable(mName
), qPrintable(p
->errorString()));
377 // Get the root component
378 QObject
* root
= p
->instance();
380 // Does the module implement the iPluginFactory interface?
381 if ((mPluginFactory
= qobject_cast
<iPluginFactory
*>(root
)) == 0) {
383 // If not, then it has to implement the iPlugin interface
384 if (qobject_cast
<iPlugin
*>(root
) == 0) {
385 EVAF_FATAL_ERROR("Module '%s' is not a valid eVaf module", qPrintable(mName
));
395 void Module::unload()
407 iPlugin
* Module::create(QString
const & name
)
409 // If the module is not loaded, load it now
417 // Does the module implement the iPluginFactory interface?
418 if (mPluginFactory
) {
419 // Use the iPluginFactory interface to create the requested interface
420 i
= qobject_cast
<iPlugin
*>(mPluginFactory
->create(name
));
422 EVAF_FATAL_ERROR("Module '%s' failed to create the iPlugin interface with name '%s'", qPrintable(mName
), qPrintable(name
));
427 // Otherwise use the root component, but only once
430 EVAF_FATAL_ERROR("Module '%s' can implement only one iPlugin interface and one with the name '%s' is already created",
431 qPrintable(mName
), qPrintable(mPlugin
->objectName()));
435 i
= qobject_cast
<iPlugin
*>(mRoot
);
437 EVAF_FATAL_ERROR("Module '%s' does not implement the iPlugin interface", qPrintable(mName
));
448 //-------------------------------------------------------------------
450 Plugin::Plugin(Module
* module, QString
const & name
, QString
const & args
)
464 mPlugin
= mModule
->create(mName
);
465 if (mPlugin
&& !mPlugin
->objectName().isEmpty())
466 mName
= mPlugin
->objectName();
470 void Plugin::unload()
479 return mPlugin
->init(mArgs
);