]> vaikene.ee Git - evaf/blob - src/libs/Plugins/pluginmanager.cpp
5d08dcb10ddff65bf4ed64d207371d139ec542d6
[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 eVaf plugins
295 int i = 0;
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);
300 }
301 else
302 ++i;
303 }
304
305 // Initialize eVaf plugins
306 i = 0;
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);
311 }
312 else
313 ++i;
314 }
315
316 return true;
317 }
318
319 void PluginManagerPrivate::unloadPlugins()
320 {
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();
326 p->unload();
327 }
328
329 // Unload all the modules
330 while (!mModules.isEmpty()) {
331 QExplicitlySharedDataPointer<Module> m = mModules.takeLast();
332 m->unload();
333 }
334 }
335
336 Module * PluginManagerPrivate::moduleByName(QString const & name) const
337 {
338 for (int i = 0; i < mModules.size(); ++i) {
339 if (mModules.at(i)->name() == name)
340 return mModules.at(i).data();
341 }
342 return 0;
343 }
344
345
346 //-------------------------------------------------------------------
347
348 Module::Module(QString const & name)
349 : QSharedData()
350 , mName(name)
351 , mLoader(0)
352 , mRoot(0)
353 , mPlugin(0)
354 , mPluginFactory(0)
355 {}
356
357 Module::~Module()
358 {
359 if (mLoader) {
360 mLoader->unload();
361 delete mLoader;
362 }
363 }
364
365 bool Module::load()
366 {
367 // The real file name with path
368 QString fileName = Common::iApp::instance()->binDir() + expandPluginName(mName);
369
370 // Try to load the module
371 QScopedPointer<QPluginLoader> p(new QPluginLoader(fileName));
372 if (!p->load()) {
373 EVAF_FATAL_ERROR("Failed to load '%s' : %s", qPrintable(mName), qPrintable(p->errorString()));
374 return false;
375 }
376
377 // Get the root component
378 QObject * root = p->instance();
379
380 // Does the module implement the iPluginFactory interface?
381 if ((mPluginFactory = qobject_cast<iPluginFactory *>(root)) == 0) {
382
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));
386 return false;
387 }
388 }
389
390 mRoot = root;
391 mLoader = p.take();
392 return true;
393 }
394
395 void Module::unload()
396 {
397 mRoot = 0;
398 mPluginFactory = 0;
399 mPlugin = 0;
400 if (mLoader) {
401 mLoader->unload();
402 delete mLoader;
403 mLoader = 0;
404 }
405 }
406
407 iPlugin * Module::create(QString const & name)
408 {
409 // If the module is not loaded, load it now
410 if (!mLoader) {
411 if (!load())
412 return 0;
413 }
414
415 iPlugin * i = 0;
416
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));
421 if (i == 0) {
422 EVAF_FATAL_ERROR("Module '%s' failed to create the iPlugin interface with name '%s'", qPrintable(mName), qPrintable(name));
423 return 0;
424 }
425 }
426
427 // Otherwise use the root component, but only once
428 else {
429 if (mPlugin) {
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()));
432 return 0;
433 }
434
435 i = qobject_cast<iPlugin *>(mRoot);
436 if (i == 0) {
437 EVAF_FATAL_ERROR("Module '%s' does not implement the iPlugin interface", qPrintable(mName));
438 return 0;
439 }
440
441 mPlugin = i;
442 }
443
444 return i;
445 }
446
447
448 //-------------------------------------------------------------------
449
450 Plugin::Plugin(Module * module, QString const & name, QString const & args)
451 : QSharedData()
452 , mModule(module)
453 , mName(name)
454 , mArgs(args)
455 , mPlugin(0)
456 {}
457
458 Plugin::~Plugin()
459 {
460 }
461
462 bool Plugin::load()
463 {
464 mPlugin = mModule->create(mName);
465 if (mPlugin && !mPlugin->objectName().isEmpty())
466 mName = mPlugin->objectName();
467 return mPlugin != 0;
468 }
469
470 void Plugin::unload()
471 {
472 mPlugin = 0;
473 }
474
475 bool Plugin::init()
476 {
477 if (!mPlugin)
478 return false;
479 return mPlugin->init(mArgs);
480 }
481
482 void Plugin::done()
483 {
484 if (mPlugin)
485 mPlugin->done();
486 }