]> vaikene.ee Git - evaf/blob - src/libs/Plugins/pluginmanager.cpp
643cad217cdfc4a353e13e74a6184689150601cc
[evaf] / src / libs / Plugins / pluginmanager.cpp
1 /**
2 * @file plugins/pluginmanager.cpp
3 * @brief Implementation of the plugin manager
4 *
5 * Copyright (c) 2011 Enar Vaikene
6 *
7 * This file is part of the eVaf C++ cross-platform application development framework.
8 *
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.
14 *
15 * Alternatively, this file may be used in accordance with the Commercial License
16 * Agreement provided with the Software.
17 */
18
19 #include "pluginmanager.h"
20 #include "pluginmanager_p.h"
21 #include "iplugin.h"
22 #include "ipluginfactory.h"
23 #include "version.h"
24
25 #include <Common/Globals>
26 #include <Common/Util>
27 #include <Common/iLogger>
28 #include <Common/iApp>
29
30 #include <QtCore>
31
32
33 namespace eVaf {
34 namespace Plugins {
35 namespace Internal {
36
37 // Plugin manager interface implementation
38 static eVaf::Plugins::PluginManager * mPluginManager = 0;
39
40 } // namespace eVaf::Plugins::Internal
41 } // namespace eVaf::Plugins
42 } // namespace eVaf
43
44
45 //-------------------------------------------------------------------
46
47 using namespace eVaf;
48 using namespace eVaf::Plugins;
49
50 PluginManager::PluginManager()
51 : QObject()
52 {
53 setObjectName(QString("%1-PluginManager").arg(VER_MODULE_NAME_STR));
54
55 Internal::mPluginManager = this;
56
57 d = new Internal::PluginManagerPrivate;
58
59 EVAF_INFO("%s created", qPrintable(objectName()));
60 }
61
62 PluginManager::~PluginManager()
63 {
64 delete d;
65
66 Internal::mPluginManager = 0;
67
68 EVAF_INFO("%s destroyed", qPrintable(objectName()));
69 }
70
71 PluginManager * PluginManager::instance()
72 {
73 return Internal::mPluginManager;
74 }
75
76 bool PluginManager::init()
77 {
78 // Initialize the internal implementation
79 if (!d->init())
80 return false;
81
82 EVAF_INFO("Loading plugins");
83
84 // Load and initialize plugins
85 if (!d->loadPlugins())
86 return false;
87
88 EVAF_INFO("Plugins loaded");
89
90 emit pluginsLoaded();
91
92 EVAF_INFO("%s initialized", qPrintable(objectName()));
93
94 return true;
95 }
96
97 void PluginManager::done()
98 {
99 EVAF_INFO("Unloading plugins");
100
101 // Finalize and unload plugins
102 d->unloadPlugins();
103
104 emit pluginsUnloaded();
105
106 EVAF_INFO("Plugins unloaded");
107
108 // Finalize the internal implementation
109 d->done();
110
111 EVAF_INFO("%s finalized", qPrintable(objectName()));
112 }
113
114
115 //-------------------------------------------------------------------
116
117 using namespace eVaf::Plugins::Internal;
118
119 PluginManagerPrivate::PluginManagerPrivate()
120 : QObject()
121 {
122 setObjectName(QString("%1-PluginManagerPrivate").arg(VER_MODULE_NAME_STR));
123
124 EVAF_INFO("%s created", qPrintable(objectName()));
125 }
126
127 PluginManagerPrivate::~PluginManagerPrivate()
128 {
129 EVAF_INFO("%s destroyed", qPrintable(objectName()));
130 }
131
132 bool PluginManagerPrivate::init()
133 {
134 EVAF_INFO("%s initialized", qPrintable(objectName()));
135
136 return true;
137 }
138
139 void PluginManagerPrivate::done()
140 {
141 EVAF_INFO("%s finalized", qPrintable(objectName()));
142 }
143
144 bool PluginManagerPrivate::loadPlugins()
145 {
146 // Get the name of the application's XML file
147 QString xmlFileName = Common::iApp::instance()->etcDir() + Common::iApp::instance()->xmlFileName();
148
149 // Open the XML file
150 QFile xmlFile(xmlFileName);
151 if (!xmlFile.open(QFile::ReadOnly)) {
152 EVAF_FATAL_ERROR("Failed to open '%s' : %s", qPrintable(xmlFileName), qPrintable(xmlFile.errorString()));
153 return false;
154 }
155
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;
162 QString moduleName;
163 QString pluginName;
164 QString args;
165 QStringList qtPlugins;
166
167 while (!xml.atEnd()) {
168 xml.readNext();
169
170 // Start element?
171 if (xml.isStartElement()) {
172
173 // Not a valid XML file yet?
174 if (!isValid) {
175 if (xml.name() == "eVaf") {
176 isValid = true;
177 }
178 else {
179 EVAF_FATAL_ERROR("'%s' is not a valid XML file for eVaf applications", qPrintable(xmlFileName));
180 return false;
181 }
182 }
183
184 // This is a valid XML file
185 else {
186
187 // No plugins or qtplugins sections yet?
188 if (!isPlugins && !isQtPlugins) {
189 if (xml.name() == "plugins") {
190 // Check for windows and linux only sections
191 #ifdef Q_OS_LINUX
192 if (Common::isTrue(xml.attributes().value("windowsonly").toString()))
193 continue;
194 #endif
195 #ifdef Q_OS_WIN32
196 if (Common::isTrue(xml.attributes().value("linuxonly").toString()))
197 continue;
198 #endif
199 isPlugins = true;
200 }
201
202 else if (xml.name() == "qtplugins") {
203 // Check for windows and linux only sections
204 #ifdef Q_OS_LINUX
205 if (Common::isTrue(xml.attributes().value("windowsonly").toString()))
206 continue;
207 #endif
208 #ifdef Q_OS_WIN32
209 if (Common::isTrue(xml.attributes().value("linuxonly").toString()))
210 continue;
211 #endif
212 isQtPlugins = true;
213 qtPlugins.clear();
214 }
215 }
216
217 // An individual plugin?
218 else if (isPlugins && xml.name() == "plugin") {
219 // Check for windows and linux only plugins
220 #ifdef Q_OS_LINUX
221 if (Common::isTrue(xml.attributes().value("windowsonly").toString())) {
222 EVAF_INFO("Plugin '%s' is for Windows only", qPrintable(xml.attributes().value("name").toString()));
223 continue;
224 }
225 #endif
226 #ifdef Q_OS_WIN32
227 if (Common::isTrue(xml.attributes().value("linuxonly").toString())) {
228 EVAF_INFO("Plugin '%s' is for Linux only", qPrintable(xml.attributes().value("name").toString()));
229 continue;
230 }
231 #endif
232
233 pluginName = xml.attributes().value("name").toString();
234 moduleName = xml.attributes().value("filename").toString();
235
236 /// @TODO: If the file name attribute is empty, loog for the config attribute
237 if (moduleName.isEmpty())
238 continue;
239
240 isPlugin = true;
241 args.clear();
242 }
243
244 // Plugin arguments?
245 else if (isPlugin) {
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() + "\"");
249 args.append(">");
250 }
251
252 // An individual Qt plugin?
253 else if (isQtPlugins && xml.name() == "plugin") {
254 // Check for windows and linux only plugins
255 #ifdef Q_OS_LINUX
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()));
258 continue;
259 }
260 #endif
261 #ifdef Q_OS_WIN32
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()));
264 continue;
265 }
266 #endif
267 QString name = xml.attributes().value("filename").toString();
268 if (!name.isEmpty() && !qtPlugins.contains(name))
269 qtPlugins.append(name);
270 }
271 }
272 } // Start element?
273
274 // End element?
275 else if (xml.isEndElement()) {
276 if (isPlugin && xml.name() == "plugin") {
277 isPlugin = false;
278 Module * m = moduleByName(moduleName);
279 if (!m)
280 mModules.append(QExplicitlySharedDataPointer<Module>(m = new Module(moduleName)));
281 mPlugins.append(QExplicitlySharedDataPointer<Plugin>(new Plugin(m, pluginName, args)));
282 }
283 else if (isPlugin)
284 args.append("</" + xml.name().toString() + ">");
285 else if (xml.name() == "plugins")
286 isPlugins = false;
287 else if (xml.name() == "qtplugins")
288 isQtPlugins = false;
289 else if (xml.name() == "eVaf")
290 isValid = false;
291 } // End element?
292 }
293
294 // Load Qt plugins
295 int i;
296 for (i = 0; i < qtPlugins.size(); ++i) {
297 loadQtPlugin(qtPlugins.at(i));
298 }
299
300 // Load eVaf plugins
301 i = 0;
302 while (i < mPlugins.size()) {
303 if (!mPlugins.at(i)->load()) {
304 EVAF_ERROR("Failed to load module '%s'", qPrintable(mPlugins.at(i)->name()));
305 mPlugins.removeAt(i);
306 }
307 else
308 ++i;
309 }
310
311 // Initialize eVaf plugins
312 i = 0;
313 while (i < mPlugins.size()) {
314 if (!mPlugins.at(i)->init()) {
315 EVAF_ERROR("Failed to initialize module '%s'", qPrintable(mPlugins.at(i)->name()));
316 mPlugins.removeAt(i);
317 }
318 else
319 ++i;
320 }
321
322 return true;
323 }
324
325 void PluginManagerPrivate::unloadPlugins()
326 {
327 // Finalize all the plugins
328 for (int i = 0; i < mPlugins.size(); ++i)
329 mPlugins.at(i)->done();
330 while (!mPlugins.isEmpty()) {
331 QExplicitlySharedDataPointer<Plugin> p = mPlugins.takeLast();
332 p->unload();
333 }
334
335 // Unload all the modules
336 while (!mModules.isEmpty()) {
337 QExplicitlySharedDataPointer<Module> m = mModules.takeLast();
338 m->unload();
339 }
340 }
341
342 Module * PluginManagerPrivate::moduleByName(QString const & name) const
343 {
344 for (int i = 0; i < mModules.size(); ++i) {
345 if (mModules.at(i)->name() == name)
346 return mModules.at(i).data();
347 }
348 return 0;
349 }
350
351 bool PluginManagerPrivate::loadQtPlugin(QString const & name) const
352 {
353 // Get the Qt plugin file name with the full path
354 QString fileName;
355
356 #ifdef Q_OS_LINUX
357 fileName = QString("%1libq%2.so").arg(Common::iApp::instance()->qtPluginsDir()).arg(name);
358 # ifndef QT_NO_DEBUG
359 QString t = QString("%1libq%2.so.debug").arg(Common::iApp::instance()->qtPluginsDir()).arg(name);
360 if (QFile::exists(t))
361 fileName = t;
362 # endif
363 #endif
364
365 #ifdef Q_OS_WIN32
366 fileName = QString("%2q%2%3").arg(Common::iApp::instance()->qtPluginsDir()).arg(name).arg("4.dll");
367 # ifndef QT_NO_DEBUG
368 QString t = QString("%1q%2%3").arg(Common::iApp::instance()->qtPluginsDir()).arg(name).arg("d4.dll");
369 if (!QFile::exists(t))
370 fileName = t;
371 # endif
372 #endif
373
374 if (fileName.isEmpty()) {
375 EVAF_ERROR("Don\'t know how to load Qt plugin '%s'", qPrintable(name));
376 return false;
377 }
378
379 EVAF_INFO("Loading Qt plugin '%s'", qPrintable(fileName));
380
381 QLibrary lib(fileName);
382 void * fn = lib.resolve("qt_plugin_instance");
383 if (fn) {
384 qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunction(fn));
385 return true;
386 }
387 else {
388 EVAF_ERROR("Failed to load Qt plugin '%s' : %s", qPrintable(fileName), qPrintable(lib.errorString()));
389 return false;
390 }
391 }
392
393
394 //-------------------------------------------------------------------
395
396 Module::Module(QString const & name)
397 : QSharedData()
398 , mName(name)
399 , mLoader(0)
400 , mRoot(0)
401 , mPlugin(0)
402 , mPluginFactory(0)
403 {}
404
405 Module::~Module()
406 {
407 if (mLoader) {
408 mLoader->unload();
409 delete mLoader;
410 }
411 }
412
413 bool Module::load()
414 {
415 // The real file name with path
416 QString fileName = Common::iApp::instance()->binDir() + expandPluginName(mName);
417
418 // Try to load the module
419 QScopedPointer<QPluginLoader> p(new QPluginLoader(fileName));
420 if (!p->load()) {
421 EVAF_FATAL_ERROR("Failed to load '%s' : %s", qPrintable(mName), qPrintable(p->errorString()));
422 return false;
423 }
424
425 // Get the root component
426 QObject * root = p->instance();
427
428 // Does the module implement the iPluginFactory interface?
429 if ((mPluginFactory = qobject_cast<iPluginFactory *>(root)) == 0) {
430
431 // If not, then it has to implement the iPlugin interface
432 if (qobject_cast<iPlugin *>(root) == 0) {
433 EVAF_FATAL_ERROR("Module '%s' is not a valid eVaf module", qPrintable(mName));
434 return false;
435 }
436 }
437
438 mRoot = root;
439 mLoader = p.take();
440 return true;
441 }
442
443 void Module::unload()
444 {
445 mRoot = 0;
446 mPluginFactory = 0;
447 mPlugin = 0;
448 if (mLoader) {
449 mLoader->unload();
450 delete mLoader;
451 mLoader = 0;
452 }
453 }
454
455 iPlugin * Module::create(QString const & name)
456 {
457 // If the module is not loaded, load it now
458 if (!mLoader) {
459 if (!load())
460 return false;
461 }
462
463 iPlugin * i = 0;
464
465 // Does the module implement the iPluginFactory interface?
466 if (mPluginFactory) {
467 // Use the iPluginFactory interface to create the requested interface
468 i = qobject_cast<iPlugin *>(mPluginFactory->create(name));
469 if (i == 0) {
470 EVAF_FATAL_ERROR("Module '%s' failed to create the iPlugin interface with name '%s'", qPrintable(mName), qPrintable(name));
471 return 0;
472 }
473 }
474
475 // Otherwise use the root component, but only once
476 else {
477 if (mPlugin) {
478 EVAF_FATAL_ERROR("Module '%s' can implement only one iPlugin interface and one with the name '%s' is already created",
479 qPrintable(mName), qPrintable(mPlugin->objectName()));
480 return 0;
481 }
482
483 i = qobject_cast<iPlugin *>(mRoot);
484 if (i == 0) {
485 EVAF_FATAL_ERROR("Module '%s' does not implement the iPlugin interface", qPrintable(mName));
486 return 0;
487 }
488
489 mPlugin = i;
490 }
491
492 return i;
493 }
494
495
496 //-------------------------------------------------------------------
497
498 Plugin::Plugin(Module * module, QString const & name, QString const & args)
499 : QSharedData()
500 , mModule(module)
501 , mName(name)
502 , mArgs(args)
503 , mPlugin(0)
504 {}
505
506 Plugin::~Plugin()
507 {
508 }
509
510 bool Plugin::load()
511 {
512 mPlugin = mModule->create(mName);
513 if (mPlugin && !mPlugin->objectName().isEmpty())
514 mName = mPlugin->objectName();
515 return mPlugin != 0;
516 }
517
518 void Plugin::unload()
519 {
520 mPlugin = 0;
521 }
522
523 bool Plugin::init()
524 {
525 if (!mPlugin)
526 return false;
527 return mPlugin->init(mArgs);
528 }
529
530 void Plugin::done()
531 {
532 if (mPlugin)
533 mPlugin->done();
534 }