1 // Object Viewer Qt - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2011 Dzmitry KAMIAHIN (dnk-88) <dnk-88@tut.by>
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010 Winch Gate Property Limited
6 // Copyright (C) 2014 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "plugin_manager.h"
22 #include "plugin_spec.h"
24 #include <QtCore/QDir>
26 #include <nel/misc/debug.h>
28 namespace ExtensionSystem
31 PluginManager::PluginManager(QObject
*parent
)
32 :IPluginManager(parent
),
38 PluginManager::~PluginManager()
43 qDeleteAll(m_pluginSpecs
);
46 void PluginManager::addObject(QObject
*obj
)
48 QWriteLocker
lock(&m_lock
);
51 nlwarning("trying to add null object");
54 if (m_allObjects
.contains(obj
))
56 nlwarning("trying to add duplicate object");
59 nlinfo("addObject: %s", obj
->objectName().toUtf8().constData());
61 m_allObjects
.append(obj
);
63 Q_EMIT
objectAdded(obj
);
66 void PluginManager::removeObject(QObject
*obj
)
70 nlwarning("trying to remove null object");
74 if (!m_allObjects
.contains(obj
))
76 nlinfo("object not in list: %s", obj
->objectName().toUtf8().constData());
79 nlinfo("removeObject: %s", obj
->objectName().toUtf8().constData());
81 Q_EMIT
aboutToRemoveObject(obj
);
82 QWriteLocker
lock(&m_lock
);
83 m_allObjects
.removeAll(obj
);
86 QList
<QObject
*> PluginManager::allObjects() const
91 void PluginManager::loadPlugins()
93 Q_FOREACH (PluginSpec
*spec
, m_pluginSpecs
)
94 setPluginState(spec
, State::Resolved
);
96 QList
<PluginSpec
*> queue
= loadQueue();
97 Q_EMIT
pluginCount( queue
.count() );
99 Q_FOREACH (PluginSpec
*spec
, queue
)
100 setPluginState(spec
, State::Loaded
);
102 Q_EMIT
pluginsLoaded();
104 Q_FOREACH (PluginSpec
*spec
, queue
)
105 setPluginState(spec
, State::Initialized
);
107 Q_EMIT
pluginsInitialized();
109 QListIterator
<PluginSpec
*> it(queue
);
111 while (it
.hasPrevious())
112 setPluginState(it
.previous(), State::Running
);
114 Q_EMIT
pluginsStarted();
116 Q_EMIT
pluginsChanged();
119 bool PluginManager::loadPluginSpec( const QString
&plugin
)
121 nlinfo( "Loading plugin spec %s", plugin
.toUtf8().data() );
123 PluginSpec
*spec
= new PluginSpec
;
124 spec
->m_pluginManager
= this;
125 if( !spec
->setSpecFileName( plugin
) )
127 nlinfo( "Error loading plugin spec %s", plugin
.toUtf8().data() );
131 m_pluginSpecs
.append( spec
);
132 m_ipluginSpecs
.append( spec
);
137 bool PluginManager::loadPlugin( const QString
&plugin
)
139 if( !loadPluginSpec( plugin
) )
142 ExtensionSystem::PluginSpec
*spec
= m_pluginSpecs
.last();
144 if( !spec
->resolveDependencies( m_pluginSpecs
) )
146 nlinfo( "Error resolving dependencies for plugin spec %s", plugin
.toUtf8().data() );
150 if( !spec
->loadLibrary() )
152 nlinfo( "Error loading plugin %s", spec
->fileName().toUtf8().data() );
156 if( !spec
->initializePlugin() )
158 nlinfo( "Error initializing plugin %s", spec
->fileName().toUtf8().data() );
163 if( !spec
->initializeExtensions() )
165 nlinfo( "Error starting plugin %s", spec
->fileName().toUtf8().data() );
171 nlinfo( "Loaded plugin %s ( %s )", spec
->name().data(), spec
->fileName().toUtf8().data() );
173 Q_EMIT
pluginsChanged();
178 bool PluginManager::unloadPlugin( ExtensionSystem::IPluginSpec
*plugin
)
180 ExtensionSystem::PluginSpec
*spec
= static_cast< ExtensionSystem::PluginSpec
* >( plugin
);
182 // Let's see if anything depends on this one
183 QListIterator
< ExtensionSystem::PluginSpec
* > itr( m_pluginSpecs
);
184 while( itr
.hasNext() )
186 ExtensionSystem::PluginSpec
*sp
= itr
.next();
187 QList
< ExtensionSystem::PluginSpec
* > l
= sp
->dependencySpecs();
188 if( l
.contains( spec
) )
192 // Stop, delete then remove plugin
195 removePlugin( plugin
);
200 void PluginManager::removePlugin( ExtensionSystem::IPluginSpec
*plugin
)
202 QList
< ExtensionSystem::IPluginSpec
* >::iterator itr1
;
203 QList
< ExtensionSystem::PluginSpec
* >::iterator itr2
;
205 QList
< ExtensionSystem::IPluginSpec
* >::iterator i1
= m_ipluginSpecs
.begin();
206 while( i1
!= m_ipluginSpecs
.end() )
216 QList
< ExtensionSystem::PluginSpec
* >::iterator i2
= m_pluginSpecs
.begin();
217 while( i2
!= m_pluginSpecs
.end() )
219 if( *i2
== static_cast< ExtensionSystem::PluginSpec
* >( plugin
) )
227 m_ipluginSpecs
.erase( itr1
);
228 m_pluginSpecs
.erase( itr2
);
231 Q_EMIT
pluginsChanged();
235 QStringList
PluginManager::getPluginPaths() const
237 return m_pluginPaths
;
240 void PluginManager::setPluginPaths(const QStringList
&paths
)
242 m_pluginPaths
= paths
;
247 QList
<IPluginSpec
*> PluginManager::plugins() const
249 return m_ipluginSpecs
;
252 void PluginManager::setSettings(QSettings
*settings
)
254 m_settings
= settings
;
257 QSettings
*PluginManager::settings() const
262 void PluginManager::readSettings()
266 QStringList blackList
;
267 m_settings
->beginGroup("PluginManager");
268 blackList
= m_settings
->value("BlackList").toStringList();
269 m_settings
->endGroup();
270 Q_FOREACH (PluginSpec
*spec
, m_pluginSpecs
)
272 QString pluginName
= spec
->fileName();
274 if (blackList
.contains(pluginName
))
276 spec
->setEnabled(false);
277 spec
->setEnabledStartup(false);
283 void PluginManager::writeSettings()
287 QStringList blackList
;
288 Q_FOREACH(PluginSpec
*spec
, m_pluginSpecs
)
290 if (!spec
->isEnabled())
291 blackList
.push_back(spec
->fileName());
293 m_settings
->beginGroup("PluginManager");
294 m_settings
->setValue("BlackList", blackList
);
295 m_settings
->endGroup();
300 void PluginManager::readPluginPaths()
302 qDeleteAll(m_pluginSpecs
);
303 m_pluginSpecs
.clear();
304 m_ipluginSpecs
.clear();
306 QStringList pluginsList
;
307 QStringList searchPaths
= m_pluginPaths
;
308 while (!searchPaths
.isEmpty())
310 const QDir
dir(searchPaths
.takeFirst());
311 const QFileInfoList files
= dir
.entryInfoList(QStringList() << QString("studio_plugin_*.%1").arg(m_extension
), QDir::Files
);
312 Q_FOREACH (const QFileInfo
&file
, files
)
313 pluginsList
<< file
.absoluteFilePath();
314 const QFileInfoList dirs
= dir
.entryInfoList(QDir::Dirs
|QDir::NoDotAndDotDot
);
315 Q_FOREACH (const QFileInfo
&subdir
, dirs
)
316 searchPaths
<< subdir
.absoluteFilePath();
319 Q_FOREACH (const QString
&pluginFile
, pluginsList
)
321 loadPluginSpec( pluginFile
.toUtf8().data() );
324 Q_EMIT
pluginsChanged();
327 void PluginManager::setPluginState(PluginSpec
*spec
, int destState
)
329 if (spec
->hasError() || spec
->state() != destState
-1)
332 // plugin in black list
333 if (!spec
->isEnabledStartup())
338 case State::Resolved
:
339 spec
->resolveDependencies(m_pluginSpecs
);
342 Q_EMIT
pluginStarting( spec
->name().toUtf8().data() );
343 spec
->initializeExtensions();
351 Q_FOREACH (const PluginSpec
*depSpec
, spec
->dependencySpecs())
353 if (depSpec
->state() != destState
)
355 spec
->m_hasError
= true;
356 spec
->m_errorString
= tr("Cannot load plugin because dependency failed to load: %1")
357 .arg(depSpec
->name());
364 Q_EMIT
pluginLoading( spec
->name().toUtf8().data() );
367 case State::Initialized
:
368 Q_EMIT
pluginInitializing( spec
->name().toUtf8().data() );
369 spec
->initializePlugin();
379 QList
<PluginSpec
*> PluginManager::loadQueue()
381 QList
<PluginSpec
*> queue
;
382 Q_FOREACH(PluginSpec
*spec
, m_pluginSpecs
)
384 QList
<PluginSpec
*> circularityCheckQueue
;
385 loadQueue(spec
, queue
, circularityCheckQueue
);
390 bool PluginManager::loadQueue(PluginSpec
*spec
, QList
<PluginSpec
*> &queue
,
391 QList
<PluginSpec
*> &circularityCheckQueue
)
393 if (queue
.contains(spec
))
395 // check for circular dependencies
396 if (circularityCheckQueue
.contains(spec
))
398 spec
->m_hasError
= true;
399 spec
->m_errorString
= tr("Circular dependency detected:\n");
400 int index
= circularityCheckQueue
.indexOf(spec
);
401 for (int i
= index
; i
< circularityCheckQueue
.size(); ++i
)
403 spec
->m_errorString
.append(tr("%1(%2) depends on\n")
404 .arg(circularityCheckQueue
.at(i
)->name()).arg(circularityCheckQueue
.at(i
)->version()));
406 spec
->m_errorString
.append(tr("%1(%2)").arg(spec
->name()).arg(spec
->version()));
409 circularityCheckQueue
.append(spec
);
410 // check if we have the dependencies
411 if (spec
->state() == State::Invalid
|| spec
->state() == State::Read
)
418 Q_FOREACH (PluginSpec
*depSpec
, spec
->dependencySpecs())
420 if (!loadQueue(depSpec
, queue
, circularityCheckQueue
))
422 spec
->m_hasError
= true;
423 spec
->m_errorString
=
424 tr("Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3")
425 .arg(depSpec
->name()).arg(depSpec
->version()).arg(depSpec
->errorString());
434 void PluginManager::stopAll()
436 QList
<PluginSpec
*> queue
= loadQueue();
437 Q_FOREACH (PluginSpec
*spec
, queue
)
438 setPluginState(spec
, State::Stopped
);
441 void PluginManager::deleteAll()
443 QList
<PluginSpec
*> queue
= loadQueue();
444 QListIterator
<PluginSpec
*> it(queue
);
446 while (it
.hasPrevious())
448 setPluginState(it
.previous(), State::Deleted
);
452 }; // namespace ExtensionSystem