]> vaikene.ee Git - evaf/blob - src/libs/Plugins/pluginmanager.cpp
dac188f4a6a4addd612fce260fc98dee043f6c1d
[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 = mPlugins.size() - 1; i >= 0; --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("%1q%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 #ifdef Q_OS_CYGWIN
375 fileName = QString("%1cygq%2.dll").arg(Common::iApp::instance()->qtPluginsDir()).arg(name);
376 # ifndef QT_NO_DEBUG
377 QString t = QString("%1libq%2.dll.debug").arg(Common::iApp::instance()->qtPluginsDir()).arg(name);
378 if (QFile::exists(t))
379 fileName = t;
380 # endif
381 #endif
382
383 if (fileName.isEmpty()) {
384 EVAF_ERROR("Don\'t know how to load Qt plugin '%s'", qPrintable(name));
385 return false;
386 }
387
388 EVAF_INFO("Loading Qt plugin '%s'", qPrintable(fileName));
389
390 QLibrary lib(fileName);
391 void * fn = lib.resolve("qt_plugin_instance");
392 if (fn) {
393 qRegisterStaticPluginInstanceFunction(QtPluginInstanceFunction(fn));
394 return true;
395 }
396 else {
397 EVAF_ERROR("Failed to load Qt plugin '%s' : %s", qPrintable(fileName), qPrintable(lib.errorString()));
398 return false;
399 }
400 }
401
402
403 //-------------------------------------------------------------------
404
405 Module::Module(QString const & name)
406 : QSharedData()
407 , mName(name)
408 , mLoader(0)
409 , mRoot(0)
410 , mPlugin(0)
411 , mPluginFactory(0)
412 {}
413
414 Module::~Module()
415 {
416 if (mLoader) {
417 mLoader->unload();
418 delete mLoader;
419 }
420 }
421
422 bool Module::load()
423 {
424 // The real file name with path
425 QString fileName = Common::iApp::instance()->binDir() + expandPluginName(mName);
426
427 // Try to load the module
428 QScopedPointer<QPluginLoader> p(new QPluginLoader(fileName));
429 if (!p->load()) {
430 EVAF_FATAL_ERROR("Failed to load '%s' : %s", qPrintable(mName), qPrintable(p->errorString()));
431 return false;
432 }
433
434 // Get the root component
435 QObject * root = p->instance();
436
437 // Does the module implement the iPluginFactory interface?
438 if ((mPluginFactory = qobject_cast<iPluginFactory *>(root)) == 0) {
439
440 // If not, then it has to implement the iPlugin interface
441 if (qobject_cast<iPlugin *>(root) == 0) {
442 EVAF_FATAL_ERROR("Module '%s' is not a valid eVaf module", qPrintable(mName));
443 return false;
444 }
445 }
446
447 mRoot = root;
448 mLoader = p.take();
449 return true;
450 }
451
452 void Module::unload()
453 {
454 mRoot = 0;
455 mPluginFactory = 0;
456 mPlugin = 0;
457 if (mLoader) {
458 mLoader->unload();
459 delete mLoader;
460 mLoader = 0;
461 }
462 }
463
464 iPlugin * Module::create(QString const & name)
465 {
466 // If the module is not loaded, load it now
467 if (!mLoader) {
468 if (!load())
469 return 0;
470 }
471
472 iPlugin * i = 0;
473
474 // Does the module implement the iPluginFactory interface?
475 if (mPluginFactory) {
476 // Use the iPluginFactory interface to create the requested interface
477 i = qobject_cast<iPlugin *>(mPluginFactory->create(name));
478 if (i == 0) {
479 EVAF_FATAL_ERROR("Module '%s' failed to create the iPlugin interface with name '%s'", qPrintable(mName), qPrintable(name));
480 return 0;
481 }
482 }
483
484 // Otherwise use the root component, but only once
485 else {
486 if (mPlugin) {
487 EVAF_FATAL_ERROR("Module '%s' can implement only one iPlugin interface and one with the name '%s' is already created",
488 qPrintable(mName), qPrintable(mPlugin->objectName()));
489 return 0;
490 }
491
492 i = qobject_cast<iPlugin *>(mRoot);
493 if (i == 0) {
494 EVAF_FATAL_ERROR("Module '%s' does not implement the iPlugin interface", qPrintable(mName));
495 return 0;
496 }
497
498 mPlugin = i;
499 }
500
501 return i;
502 }
503
504
505 //-------------------------------------------------------------------
506
507 Plugin::Plugin(Module * module, QString const & name, QString const & args)
508 : QSharedData()
509 , mModule(module)
510 , mName(name)
511 , mArgs(args)
512 , mPlugin(0)
513 {}
514
515 Plugin::~Plugin()
516 {
517 }
518
519 bool Plugin::load()
520 {
521 mPlugin = mModule->create(mName);
522 if (mPlugin && !mPlugin->objectName().isEmpty())
523 mName = mPlugin->objectName();
524 return mPlugin != 0;
525 }
526
527 void Plugin::unload()
528 {
529 mPlugin = 0;
530 }
531
532 bool Plugin::init()
533 {
534 if (!mPlugin)
535 return false;
536 return mPlugin->init(mArgs);
537 }
538
539 void Plugin::done()
540 {
541 if (mPlugin)
542 mPlugin->done();
543 }