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