PageRenderTime 35ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/mclient/src/clientcore/PluginSession.cpp

http://mclient-mume.googlecode.com/
C++ | 420 lines | 269 code | 86 blank | 65 comment | 57 complexity | 435e4a5dc0b45b54ed4c029ceb36b2e2 MD5 | raw file
Possible License(s): LGPL-2.1
  1. #include "PluginSession.h"
  2. // World
  3. #include "ConfigManager.h"
  4. #include "PluginManager.h"
  5. #include "MainWindow.h"
  6. // Session
  7. #include "CommandProcessor.h"
  8. #include "ActionManager.h"
  9. #include "AliasManager.h"
  10. #include "MClientPluginInterface.h"
  11. #include "PluginEntry.h"
  12. #include "ConfigEntry.h"
  13. #include "MClientEventHandler.h"
  14. #include "MClientEngineEvent.h"
  15. #include "MClientEvent.h"
  16. #include <QApplication>
  17. #include <QHash>
  18. #include <QDir>
  19. #include <QPluginLoader>
  20. #include <QDebug>
  21. #include <QVariant>
  22. PluginSession::PluginSession(const QString &s, PluginManager *pm)
  23. : AbstractPluginSession(pm), _session(s), _pluginManager(pm) {
  24. // This is usually mume
  25. _mume = true;
  26. // Create alias and action managers
  27. _aliasManager = new AliasManager(this);
  28. _actionManager = new ActionManager(this);
  29. // Load aliases and actions (temporarily uses plugin code)
  30. getManager()->getConfig()->readPluginSettings(_session, "alias");
  31. getManager()->getConfig()->readPluginSettings(_session, "action");
  32. _aliasManager->loadSettings(*getManager()->
  33. getConfig()->
  34. pluginSettings(_session, "alias")->hash());
  35. _actionManager->loadSettings(*getManager()->
  36. getConfig()->
  37. pluginSettings(_session, "action")->hash());
  38. // Create the command processor
  39. _commandProcessor = new CommandProcessor(this);
  40. _commandProcessor->start();
  41. connect(_commandProcessor, SIGNAL(quit()),
  42. _pluginManager->getMainWindow(), SLOT(close()));
  43. // Start the session in another thread to allow for widgets to be created
  44. connect(this, SIGNAL(doneLoading(PluginSession *)),
  45. _pluginManager->getMainWindow(),
  46. SLOT(initDisplay(PluginSession *)));
  47. connect(_pluginManager->getMainWindow(), SIGNAL(doneLoading()),
  48. SLOT(doneLoading()));
  49. qDebug() << "* PluginSession" << _session
  50. << "created with thread:" << QThread::currentThread();
  51. }
  52. PluginSession::~PluginSession() {
  53. /* This is unecessary because the plugin might still be in use by other sessions, right?
  54. // Unload all plugins
  55. foreach(QPluginLoader *pm, _loadedPlugins) {
  56. delete pm->instance();
  57. pm->unload();
  58. delete pm;
  59. }
  60. */
  61. exit();
  62. wait();
  63. deleteLater();
  64. qDebug() << "* PluginSession" << _session << "destroyed";
  65. }
  66. // We want to start an event loop in a separate thread to handle plugins
  67. void PluginSession::run() {
  68. loadAllPlugins();
  69. startSession();
  70. qDebug() << "* PluginSession" << _session
  71. << "is running with thread:" << QThread::currentThread();
  72. emit doneLoading(this);
  73. exec();
  74. }
  75. void PluginSession::loadAllPlugins() {
  76. // Get the needed plugins for this profile
  77. SettingsHash *hash = getManager()->getConfig()->profileSettings(_session)->hash();
  78. QStringList pluginsToLoad;
  79. int pluginsSize = hash->value("profile/plugins/size", 0).toInt();
  80. for (int i = 0; i < pluginsSize; ++i) {
  81. // Add the plugin to the list of those to be loaded
  82. QString pluginName(hash->value("profile/plugin/"+
  83. QString::number(i+1)+
  84. "/name").toString());
  85. pluginsToLoad << pluginName;
  86. }
  87. // Get a hash of the available plugins
  88. const QHash<QString, PluginEntry>& availablePlugins = _pluginManager->getAvailablePlugins();
  89. if (pluginsToLoad.isEmpty()) {
  90. pluginsToLoad = availablePlugins.keys();
  91. qDebug() << "! Profile config was blank; all plugins will be loaded!";
  92. }
  93. qDebug() << "* PluginSession" << _session
  94. << "needs plugins" << pluginsToLoad;
  95. // Load the plugins in question
  96. bool foundAllPlugins = true;
  97. foreach(QString s, pluginsToLoad) {
  98. if (availablePlugins.contains(s)) {
  99. // Make sure the plugin wasn't already loaded as a dependency
  100. if (!_loadedPlugins.contains(s)) {
  101. // Load the plugin's configuration
  102. getManager()->getConfig()->readPluginSettings(_session, s);
  103. const PluginEntry& pe = availablePlugins.value(s);
  104. if (!loadPlugin(pe.libName())) {
  105. qCritical() << "! Could not load plugin" << pe.shortName();
  106. // TODO: what should happen if we cannot load a plugin?
  107. }
  108. }
  109. }
  110. else {
  111. qCritical() << "! Unable to find plugin" << s
  112. << "for session" << _session;
  113. foundAllPlugins = false;
  114. }
  115. }
  116. if (!foundAllPlugins)
  117. qCritical() << "! Available plugins are:" << availablePlugins.keys();
  118. }
  119. bool PluginSession::loadPlugin(const QString& libName) {
  120. qDebug() << "loadPlugin call for" << libName
  121. << "for session" << _session;
  122. QString fileName = _pluginManager->getConfig()->
  123. getPluginPath() + "/" + libName;
  124. // Try to load plugin from file
  125. QPluginLoader* loader = new QPluginLoader(fileName);
  126. // NOTE: can't have 'this' as parent because PluginSession is in a
  127. // different thread than its own.
  128. if(!loader->load()) {
  129. qWarning() << "! Failed to load plugin at" << fileName;
  130. delete loader;
  131. return false;
  132. } else {
  133. MClientPluginInterface* iPlugin
  134. = qobject_cast<MClientPluginInterface*>(loader->instance());
  135. // Is it the correct kind of plugin?
  136. if(!iPlugin) {
  137. qWarning() << "! Plugin in file" << fileName
  138. << "does not implement interface";
  139. loader->unload();
  140. delete loader;
  141. return false;
  142. } else {
  143. // Check dependencies
  144. if (!checkDependencies(iPlugin))
  145. return false;
  146. // insert APIs for this plugin into hash
  147. QHash<QString, int> apis = iPlugin->implemented();
  148. QHash<QString, int>::iterator jt = apis.begin();
  149. for(; jt!=apis.end(); ++jt) {
  150. _pluginAPIs.insert(jt.key(), loader);
  151. }
  152. // Insert shortname for this plugin into hash
  153. _loadedPlugins.insert(iPlugin->shortName(), loader);
  154. qDebug() << "Successfully loaded plugin" << iPlugin->shortName();
  155. } // casted
  156. } // loaded
  157. return true;
  158. }
  159. bool PluginSession::checkDependencies(MClientPluginInterface *iPlugin) {
  160. QHash<QString, int> deps = iPlugin->dependencies();
  161. // Check dependencies
  162. QHash<QString, int>::iterator it = deps.begin();
  163. for (; it != deps.end(); ++it) {
  164. // Ask what APIs each plugin implements and compare versions
  165. foreach(const PluginEntry& pe, _pluginManager->getAvailablePlugins()) {
  166. int version = pe.version(it.key());
  167. if (version) {
  168. // Found one of the APIs, so check the version
  169. // We want the version of this plugin to be <= the version of
  170. // the one we're checking against.
  171. // Incompatible updates should change the API name.
  172. if (version > it.value()) {
  173. qDebug() << "Dependency version mismatch!";
  174. continue;
  175. }
  176. qDebug() << "Dependency" << it.key() << it.value()
  177. << "satisfied by" << pe.shortName();
  178. if (_loadedPlugins.find(pe.shortName()) == _loadedPlugins.end()) {
  179. qDebug() << "Attempting to load dependency" << pe.libName();
  180. if (!loadPlugin(pe.libName())) {
  181. qWarning() << "! Could not load deps for" << it.key();
  182. return false;
  183. }
  184. }
  185. }
  186. }
  187. }
  188. return true;
  189. }
  190. void PluginSession::startSession() {
  191. // Go through each loaded plugin and start it
  192. foreach(QPluginLoader *loader, _loadedPlugins) {
  193. MClientPluginInterface* iPlugin
  194. = qobject_cast<MClientPluginInterface*>(loader->instance());
  195. if (iPlugin) {
  196. // Start the sessions within the thread
  197. iPlugin->startSession(this);
  198. // Receive the event handler
  199. MClientEventHandler* eventHandler = iPlugin->getEventHandler(_session);
  200. if (!eventHandler) {
  201. qWarning() << "* eventHandler was invalid for " << iPlugin->shortName();
  202. continue;
  203. }
  204. // Insert datatypes this plugin wants into hash
  205. if(!iPlugin->receivesDataTypes().isEmpty()) {
  206. /*
  207. qDebug() << ">> receiving for " << iPlugin->shortName()
  208. << "types" << iPlugin->receivesDataTypes();
  209. */
  210. QHash<QString, int> hash = iPlugin->receivesDataTypes();
  211. QHash<QString, int>::const_iterator i = hash.constBegin();
  212. while (i != hash.constEnd()) {
  213. _receivesTypes[i.key()].insertMulti(i.value(), eventHandler);
  214. ++i;
  215. }
  216. }
  217. // Register the commands
  218. _commandProcessor->registerCommand(iPlugin->shortName(),
  219. iPlugin->commandEntries());
  220. }
  221. }
  222. qDebug() << "* Populating event chains...";
  223. // Identify the head of the event handler command chain
  224. QHash<QString, QMultiMap<int, MClientEventHandler*> >::const_iterator i
  225. = _receivesTypes.constBegin();
  226. while (i != _receivesTypes.constEnd()) {
  227. MClientEventHandler *previous = NULL;
  228. QMultiMap<int, MClientEventHandler*> map = i.value();
  229. QMultiMap<int, MClientEventHandler*>::const_iterator j = map.constBegin();
  230. while (j != map.constEnd()) {
  231. // If we start at the front then this is the head
  232. if (previous == NULL) _receivesType.insert(i.key(), j.value());
  233. else previous->setNextHandler(i.key(), j.value());
  234. // Keep track of the previously seen event handler
  235. previous = j.value();
  236. ++j;
  237. }
  238. // Update the tail of the command chain
  239. previous->setNextHandler(i.key(), NULL);
  240. ++i;
  241. }
  242. }
  243. void PluginSession::stopSession() {
  244. qDebug() << "* PluginSession" << _session << "is stopping...";
  245. // Save profile settings
  246. getManager()->getConfig()->writeProfileSettings(_session);
  247. foreach(QPluginLoader *pl, _loadedPlugins) {
  248. MClientPluginInterface *pi
  249. = qobject_cast<MClientPluginInterface*>(pl->instance());
  250. if (pi) {
  251. // TODO
  252. //pi->saveSettings();
  253. qDebug() << "* sending stop to" << pi->shortName();
  254. pi->stopSession(_session);
  255. }
  256. }
  257. qDebug() << "* All" << _session << "plugins have been stopped.";
  258. }
  259. const QPluginLoader* PluginSession::pluginWithAPI(const QString& api) const {
  260. QPluginLoader* plugin = _pluginAPIs.find(api).value();
  261. return plugin;
  262. }
  263. void PluginSession::customEvent(QEvent* e) {
  264. if (e->type() == 10000) {
  265. MClientEngineEvent* ee = static_cast<MClientEngineEvent*>(e);
  266. if (ee->dataType() == EE_MANAGER_POST) {
  267. }
  268. else if (ee->dataType() == EE_DATATYPE_UPDATE) {
  269. }
  270. }
  271. else if (e->type() == 10001) {
  272. MClientEvent* me = static_cast<MClientEvent*>(e);
  273. //qDebug() << "* copying posted event with payload" << me->payload();
  274. bool found = false;
  275. foreach (QString s, me->dataTypes()) {
  276. // Iterate through all the data types
  277. //qDebug() << "* finding data type" << s << "out of" << me->dataTypes();
  278. QHash<QString, MClientEventHandler*>::iterator it = _receivesType.find(s);
  279. while (it != _receivesType.end() && it.key() == s) {
  280. MClientEvent* nme = new MClientEvent(*me);
  281. //qDebug() << "* copied payload to" << nme->payload();
  282. // Need to make a copy, since the original event
  283. // will be deleted when this function returns
  284. /*
  285. qDebug() << "* posting" << nme->dataTypes() << "to"
  286. << it.value() << "with" << me->payload();
  287. */
  288. // Post the event
  289. QCoreApplication::postEvent(it.value(), nme);
  290. found = true;
  291. ++it; // Iterate
  292. }
  293. }
  294. // TODO: Improve this somehow. This is very hack-ish.
  295. if (me->dataTypes().contains("SocketConnected") ||
  296. me->dataTypes().contains("SocketDisconnected") ||
  297. me->dataTypes().contains("UnlockProcessor")) {
  298. MClientEvent* nme = new MClientEvent(*me);
  299. QCoreApplication::postEvent(_commandProcessor, nme);
  300. nme = new MClientEvent(*me);
  301. qDebug() << "* posting to CommandProcessor";
  302. found = true;
  303. }
  304. if (!found) {
  305. if (me->dataTypes().contains("XMLAll")) {
  306. MClientEvent* nme = new MClientEvent(*me);
  307. QCoreApplication::postEvent(_commandProcessor, nme);
  308. nme = new MClientEvent(*me);
  309. qDebug() << "* posting to CommandProcessor";
  310. }
  311. else if (me->dataTypes().contains("MMapperInput")) {
  312. // HACK to allow client to run without MMapper
  313. QHash<QString, MClientEventHandler*>::iterator it
  314. = _receivesType.find("SocketWriteData");
  315. while (it != _receivesType.end() && it.key() == "SocketWriteData") {
  316. MClientEvent* nme = new MClientEvent(*me);
  317. QCoreApplication::postEvent(it.value(), nme);
  318. ++it; // Iterate
  319. }
  320. }
  321. else qWarning() << "! No plugins accepted data types" << me->dataTypes();
  322. }
  323. }
  324. }
  325. ConfigEntry* PluginSession::retrievePluginSettings(const QString &pluginName) const {
  326. return _pluginManager->getConfig()->pluginSettings(_session, pluginName);
  327. }
  328. void PluginSession::doneLoading() {
  329. QVariant *payload = new QVariant();
  330. QStringList sl("DoneLoading");
  331. MClientEventData *med = new MClientEventData(payload, sl, _session);
  332. MClientEvent* me = new MClientEvent(med);
  333. customEvent(me);
  334. }