]> vaikene.ee Git - evaf/blob - src/libs/Plugins/pluginmanager.cpp
Mac OS changes and switched to c++11.
[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 = nullptr;
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 , d(new Internal::PluginManagerPrivate)
53 {
54 setObjectName(QString("%1-PluginManager").arg(VER_MODULE_NAME_STR));
55
56 Internal::mPluginManager = this;
57
58 EVAF_INFO("%s created", qPrintable(objectName()));
59 }
60
61 PluginManager::~PluginManager()
62 {
63 d.reset();
64
65 Internal::mPluginManager = nullptr;
66
67 EVAF_INFO("%s destroyed", qPrintable(objectName()));
68 }
69
70 PluginManager * PluginManager::instance()
71 {
72 return Internal::mPluginManager;
73 }
74
75 bool PluginManager::init()
76 {
77 // Initialize the internal implementation
78 if (!d->init())
79 return false;
80
81 EVAF_INFO("Loading plugins");
82
83 // Load and initialize plugins
84 if (!d->loadPlugins())
85 return false;
86
87 EVAF_INFO("Plugins loaded");
88
89 emit pluginsLoaded();
90
91 EVAF_INFO("%s initialized", qPrintable(objectName()));
92
93 return true;
94 }
95
96 void PluginManager::done()
97 {
98 EVAF_INFO("Unloading plugins");
99
100 // Finalize and unload plugins
101 d->unloadPlugins();
102
103 emit pluginsUnloaded();
104
105 EVAF_INFO("Plugins unloaded");
106
107 // Finalize the internal implementation
108 d->done();
109
110 EVAF_INFO("%s finalized", qPrintable(objectName()));
111 }
112
113
114 //-------------------------------------------------------------------
115
116 using namespace eVaf::Plugins::Internal;
117
118 PluginManagerPrivate::PluginManagerPrivate()
119 : QObject()
120 {
121 setObjectName(QString("%1-PluginManagerPrivate").arg(VER_MODULE_NAME_STR));
122
123 EVAF_INFO("%s created", qPrintable(objectName()));
124 }
125
126 PluginManagerPrivate::~PluginManagerPrivate()
127 {
128 EVAF_INFO("%s destroyed", qPrintable(objectName()));
129 }
130
131 bool PluginManagerPrivate::init()
132 {
133 EVAF_INFO("%s initialized", qPrintable(objectName()));
134
135 return true;
136 }
137
138 void PluginManagerPrivate::done()
139 {
140 EVAF_INFO("%s finalized", qPrintable(objectName()));
141 }
142
143 bool PluginManagerPrivate::loadPlugins()
144 {
145 // Get the name of the application's XML file
146 QString xmlFileName = Common::iApp::instance()->etcDir() + Common::iApp::instance()->xmlFileName();
147
148 // Open the XML file
149 QFile xmlFile(xmlFileName);
150 if (!xmlFile.open(QFile::ReadOnly)) {
151 EVAF_FATAL_ERROR("Failed to open '%s' : %s", qPrintable(xmlFileName), qPrintable(xmlFile.errorString()));
152 return false;
153 }
154
155 // Process the XML file
156 QXmlStreamReader xml(&xmlFile);
157 bool isValid = false;
158 bool isPlugins = false;
159 bool isQtPlugins = false;
160 bool isPlugin = false;
161 QString moduleName;
162 QString pluginName;
163 QString args;
164 QStringList qtPlugins;
165
166 while (!xml.atEnd()) {
167 xml.readNext();
168
169 // Start element?
170 if (xml.isStartElement()) {
171
172 // Not a valid XML file yet?
173 if (!isValid) {
174 if (xml.name() == "eVaf") {
175 isValid = true;
176 }
177 else {
178 EVAF_FATAL_ERROR("'%s' is not a valid XML file for eVaf applications", qPrintable(xmlFileName));
179 return false;
180 }
181 }
182
183 // This is a valid XML file
184 else {
185
186 // No plugins or qtplugins sections yet?
187 if (!isPlugins && !isQtPlugins) {
188 if (xml.name() == "plugins") {
189 // Check for windows and linux only sections
190 #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
191 if (Common::isTrue(xml.attributes().value("windowsonly").toString()))
192 continue;
193 #endif
194 #ifdef Q_OS_WIN32
195 if (Common::isTrue(xml.attributes().value("linuxonly").toString()))
196 continue;
197 #endif
198 isPlugins = true;
199 }
200
201 else if (xml.name() == "qtplugins") {
202 // Check for windows and linux only sections
203 #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
204 if (Common::isTrue(xml.attributes().value("windowsonly").toString()))
205 continue;
206 #endif
207 #ifdef Q_OS_WIN32
208 if (Common::isTrue(xml.attributes().value("linuxonly").toString()))
209 continue;
210 #endif
211 isQtPlugins = true;
212 qtPlugins.clear();
213 }
214 }
215
216 // An individual plugin?
217 else if (isPlugins && xml.name() == "plugin") {
218 // Check for windows and linux only plugins
219 #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
220 if (Common::isTrue(xml.attributes().value("windowsonly").toString())) {
221 EVAF_INFO("Plugin '%s' is for Windows only", qPrintable(xml.attributes().value("name").toString()));
222 continue;
223 }
224 #endif
225 #ifdef Q_OS_WIN32
226 if (Common::isTrue(xml.attributes().value("linuxonly").toString())) {
227 EVAF_INFO("Plugin '%s' is for Linux only", qPrintable(xml.attributes().value("name").toString()));
228 continue;
229 }
230 #endif
231
232 pluginName = xml.attributes().value("name").toString();
233 moduleName = xml.attributes().value("filename").toString();
234
235 /// @TODO: If the file name attribute is empty, loog for the config attribute
236 if (moduleName.isEmpty())
237 continue;
238
239 isPlugin = true;
240 args.clear();
241 }
242
243 // Plugin arguments?
244 else if (isPlugin) {
245 args.append("<" + xml.name().toString());
246 for (int i = 0; i < xml.attributes().size(); ++i)
247 args.append(" " + xml.attributes().at(i).name().toString() + "=\"" + xml.attributes().at(i).value().toString() + "\"");
248 args.append(">");
249 }
250
251 // An individual Qt plugin?
252 else if (isQtPlugins && xml.name() == "plugin") {
253 // Check for windows and linux only plugins
254 #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
255 if (Common::isTrue(xml.attributes().value("windowsonly").toString())) {
256 EVAF_INFO("Qt plugin '%s' is for Windows only", qPrintable(xml.attributes().value("name").toString()));
257 continue;
258 }
259 #endif
260 #ifdef Q_OS_WIN32
261 if (Common::isTrue(xml.attributes().value("linuxonly").toString())) {
262 EVAF_INFO("Qt plugin '%s' is for Linux only", qPrintable(xml.attributes().value("name").toString()));
263 continue;
264 }
265 #endif
266 QString name = xml.attributes().value("filename").toString();
267 if (!name.isEmpty() && !qtPlugins.contains(name))
268 qtPlugins.append(name);
269 }
270 }
271 } // Start element?
272
273 // End element?
274 else if (xml.isEndElement()) {
275 if (isPlugin && xml.name() == "plugin") {
276 isPlugin = false;
277 Module * m = moduleByName(moduleName);
278 if (!m)
279 mModules.append(QExplicitlySharedDataPointer<Module>(m = new Module(moduleName)));
280 mPlugins.append(QExplicitlySharedDataPointer<Plugin>(new Plugin(m, pluginName, args)));
281 }
282 else if (isPlugin)
283 args.append("</" + xml.name().toString() + ">");
284 else if (xml.name() == "plugins")
285 isPlugins = false;
286 else if (xml.name() == "qtplugins")
287 isQtPlugins = false;
288 else if (xml.name() == "eVaf")
289 isValid = false;
290 } // End element?
291 }
292
293 // Load eVaf plugins
294 int i = 0;
295 while (i < mPlugins.size()) {
296 if (!mPlugins.at(i)->load()) {
297 EVAF_ERROR("Failed to load module '%s'", qPrintable(mPlugins.at(i)->name()));
298 mPlugins.removeAt(i);
299 }
300 else
301 ++i;
302 }
303
304 // Initialize eVaf plugins
305 i = 0;
306 while (i < mPlugins.size()) {
307 if (!mPlugins.at(i)->init()) {
308 EVAF_ERROR("Failed to initialize module '%s'", qPrintable(mPlugins.at(i)->name()));
309 mPlugins.removeAt(i);
310 }
311 else
312 ++i;
313 }
314
315 return true;
316 }
317
318 void PluginManagerPrivate::unloadPlugins()
319 {
320 // Finalize all the plugins
321 for (int i = mPlugins.size() - 1; i >= 0; --i)
322 mPlugins.at(i)->done();
323 while (!mPlugins.isEmpty()) {
324 QExplicitlySharedDataPointer<Plugin> p = mPlugins.takeLast();
325 p->unload();
326 }
327
328 // Unload all the modules
329 while (!mModules.isEmpty()) {
330 QExplicitlySharedDataPointer<Module> m = mModules.takeLast();
331 m->unload();
332 }
333 }
334
335 Module * PluginManagerPrivate::moduleByName(QString const & name) const
336 {
337 for (int i = 0; i < mModules.size(); ++i) {
338 if (mModules.at(i)->name() == name)
339 return mModules.at(i).data();
340 }
341 return nullptr;
342 }
343
344
345 //-------------------------------------------------------------------
346
347 Module::Module(QString const & name)
348 : QSharedData()
349 , mName(name)
350 , mRoot(nullptr)
351 , mPlugin(nullptr)
352 , mPluginFactory(nullptr)
353 {}
354
355 Module::~Module()
356 {
357 if (mLoader) {
358 mLoader->unload();
359 mLoader.reset();
360 }
361 }
362
363 bool Module::load()
364 {
365 // The real file name with path
366 QString fileName = Common::iApp::instance()->binDir() + expandPluginName(mName);
367
368 // Try to load the module
369 QScopedPointer<QPluginLoader> p(new QPluginLoader(fileName));
370 if (!p->load()) {
371 EVAF_FATAL_ERROR("Failed to load '%s' : %s", qPrintable(mName), qPrintable(p->errorString()));
372 return false;
373 }
374
375 // Get the root component
376 QObject * root = p->instance();
377
378 // Does the module implement the iPluginFactory interface?
379 if ((mPluginFactory = qobject_cast<iPluginFactory *>(root)) == nullptr) {
380
381 // If not, then it has to implement the iPlugin interface
382 if (qobject_cast<iPlugin *>(root) == nullptr) {
383 EVAF_FATAL_ERROR("Module '%s' is not a valid eVaf module", qPrintable(mName));
384 return false;
385 }
386 }
387
388 mRoot = root;
389 mLoader.reset(p.take());
390 return true;
391 }
392
393 void Module::unload()
394 {
395 mRoot = nullptr;
396 mPluginFactory = nullptr;
397 mPlugin = nullptr;
398 if (mLoader) {
399 mLoader->unload();
400 mLoader.reset();
401 }
402 }
403
404 iPlugin * Module::create(QString const & name)
405 {
406 // If the module is not loaded, load it now
407 if (!mLoader) {
408 if (!load())
409 return nullptr;
410 }
411
412 iPlugin * i = nullptr;
413
414 // Does the module implement the iPluginFactory interface?
415 if (mPluginFactory) {
416 // Use the iPluginFactory interface to create the requested interface
417 i = qobject_cast<iPlugin *>(mPluginFactory->create(name));
418 if (i == nullptr) {
419 EVAF_FATAL_ERROR("Module '%s' failed to create the iPlugin interface with name '%s'", qPrintable(mName), qPrintable(name));
420 return nullptr;
421 }
422 }
423
424 // Otherwise use the root component, but only once
425 else {
426 if (mPlugin) {
427 EVAF_FATAL_ERROR("Module '%s' can implement only one iPlugin interface and one with the name '%s' is already created",
428 qPrintable(mName), qPrintable(mPlugin->objectName()));
429 return nullptr;
430 }
431
432 i = qobject_cast<iPlugin *>(mRoot);
433 if (i == nullptr) {
434 EVAF_FATAL_ERROR("Module '%s' does not implement the iPlugin interface", qPrintable(mName));
435 return nullptr;
436 }
437
438 mPlugin = i;
439 }
440
441 return i;
442 }
443
444
445 //-------------------------------------------------------------------
446
447 Plugin::Plugin(Module * module, QString const & name, QString const & args)
448 : QSharedData()
449 , mModule(module)
450 , mName(name)
451 , mArgs(args)
452 , mPlugin(nullptr)
453 {}
454
455 Plugin::~Plugin()
456 {
457 }
458
459 bool Plugin::load()
460 {
461 mPlugin = mModule->create(mName);
462 if (mPlugin && !mPlugin->objectName().isEmpty())
463 mName = mPlugin->objectName();
464 return mPlugin != nullptr;
465 }
466
467 void Plugin::unload()
468 {
469 mPlugin = nullptr;
470 }
471
472 bool Plugin::init()
473 {
474 if (!mPlugin)
475 return false;
476 return mPlugin->init(mArgs);
477 }
478
479 void Plugin::done()
480 {
481 if (mPlugin)
482 mPlugin->done();
483 }