1 /**************************************************************************************** 2 * Copyright (c) 2004-2013 Mark Kretschmann <kretschmann@kde.org> * 3 * * 4 * This program is free software; you can redistribute it and/or modify it under * 5 * the terms of the GNU General Public License as published by the Free Software * 6 * Foundation; either version 2 of the License, or (at your option) any later * 7 * version. * 8 * * 9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 11 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 12 * * 13 * You should have received a copy of the GNU General Public License along with * 14 * this program. If not, see <http://www.gnu.org/licenses/>. * 15 ****************************************************************************************/ 16 17 #define DEBUG_PREFIX "PluginManager" 18 19 #include "PluginManager.h" 20 21 #include <core/support/Amarok.h> 22 #include <core/support/Components.h> 23 #include <core/support/Debug.h> 24 #include <core-impl/collections/support/CollectionManager.h> 25 #include <core-impl/storage/StorageManager.h> 26 #include <services/ServiceBase.h> 27 #include <services/ServicePluginManager.h> 28 #include <statsyncing/Controller.h> 29 #include <statsyncing/ProviderFactory.h> 30 #include <storage/StorageFactory.h> 31 32 #include <KLocalizedString> 33 #include <KMessageBox> 34 #include <KPluginLoader> 35 36 #include <QGuiApplication> 37 38 39 /** Defines the used plugin version number. 40 * 41 * This must match the desktop files. 42 */ 43 const int Plugins::PluginManager::s_pluginFrameworkVersion = 74; 44 Plugins::PluginManager* Plugins::PluginManager::s_instance = nullptr; 45 46 Plugins::PluginManager* instance()47Plugins::PluginManager::instance() 48 { 49 return s_instance ? s_instance : new PluginManager(); 50 } 51 52 void destroy()53Plugins::PluginManager::destroy() 54 { 55 if( s_instance ) 56 { 57 delete s_instance; 58 s_instance = nullptr; 59 } 60 } 61 PluginManager(QObject * parent)62Plugins::PluginManager::PluginManager( QObject *parent ) 63 : QObject( parent ) 64 { 65 DEBUG_BLOCK 66 setObjectName( "PluginManager" ); 67 s_instance = this; 68 69 PERF_LOG( "Initialising Plugin Manager" ) 70 init(); 71 PERF_LOG( "Initialised Plugin Manager" ) 72 } 73 ~PluginManager()74Plugins::PluginManager::~PluginManager() 75 { 76 // tell the managers to get rid of their current factories 77 QList<QSharedPointer<Plugins::PluginFactory> > emptyFactories; 78 79 StatSyncing::Controller *controller = Amarok::Components::statSyncingController(); 80 if( controller ) 81 controller->setFactories( emptyFactories ); 82 ServicePluginManager::instance()->setFactories( emptyFactories ); 83 CollectionManager::instance()->setFactories( emptyFactories ); 84 StorageManager::instance()->setFactories( emptyFactories ); 85 } 86 87 void init()88Plugins::PluginManager::init() 89 { 90 checkPluginEnabledStates(); 91 } 92 93 KPluginInfo::List plugins(Type type) const94Plugins::PluginManager::plugins( Type type ) const 95 { 96 KPluginInfo::List infos; 97 98 for( const auto &pluginInfo : m_pluginsByType.value( type ) ) 99 { 100 auto info = KPluginInfo( pluginInfo ); 101 info.setConfig( Amarok::config( "Plugins" ) ); 102 infos << info; 103 } 104 105 return infos; 106 } 107 108 QVector<KPluginMetaData> enabledPlugins(Plugins::PluginManager::Type type) const109Plugins::PluginManager::enabledPlugins(Plugins::PluginManager::Type type) const 110 { 111 QVector<KPluginMetaData> enabledList; 112 113 for( const auto &plugin : m_pluginsByType.value( type ) ) 114 { 115 if( isPluginEnabled( plugin ) ) 116 enabledList << plugin; 117 } 118 119 return enabledList; 120 } 121 122 QList<QSharedPointer<Plugins::PluginFactory> > factories(Type type) const123Plugins::PluginManager::factories( Type type ) const 124 { 125 return m_factoriesByType.value( type ); 126 } 127 128 void checkPluginEnabledStates()129Plugins::PluginManager::checkPluginEnabledStates() 130 { 131 DEBUG_BLOCK 132 133 // re-create all the member infos. 134 m_plugins.clear(); 135 m_pluginsByType.clear(); 136 m_factoriesByType.clear(); 137 138 m_plugins = findPlugins(); // reload all the plugins plus their enabled state 139 140 if( m_plugins.isEmpty() ) // exit if no plugins are found 141 { 142 if( qobject_cast<QGuiApplication*>( qApp ) ) 143 { 144 KMessageBox::error( 0, i18n( "Amarok could not find any plugins. This indicates an installation problem." ) ); 145 } 146 else 147 { 148 warning() << "Amarok could not find any plugins. Bailing out."; 149 } 150 // don't use QApplication::exit, as the eventloop may not have started yet 151 std::exit( EXIT_SUCCESS ); 152 } 153 154 // sort the plugin infos by type 155 for( const auto &pluginInfo : m_plugins ) 156 { 157 // create the factories and sort them by type 158 auto factory = createFactory( pluginInfo ); 159 160 if( factory ) 161 { 162 Type type; 163 164 if( qobject_cast<StorageFactory*>( factory ) ) 165 type = Storage; 166 else if( qobject_cast<Collections::CollectionFactory*>( factory ) ) 167 type = Collection; 168 else if( qobject_cast<ServiceFactory*>( factory ) ) 169 type = Service; 170 else if( qobject_cast<StatSyncing::ProviderFactory*>( factory ) ) 171 type = Importer; 172 else 173 { 174 warning() << pluginInfo.name() << "has unknown category"; 175 warning() << pluginInfo.rawData().keys(); 176 continue; 177 } 178 179 m_pluginsByType[ type ] << pluginInfo; 180 181 if( isPluginEnabled( pluginInfo ) ) 182 m_factoriesByType[ type ] << factory; 183 } 184 else 185 warning() << pluginInfo.name() << "could not create factory"; 186 } 187 188 // the setFactories functions should: 189 // - filter out factories not useful (e.g. services when setting collections) 190 // - handle the new list of factories, disabling old ones and enabling new ones. 191 192 193 PERF_LOG( "Loading storage plugins" ) 194 StorageManager::instance()->setFactories( m_factoriesByType.value( Storage ) ); 195 PERF_LOG( "Loaded storage plugins" ) 196 197 PERF_LOG( "Loading collection plugins" ) 198 CollectionManager::instance()->setFactories( m_factoriesByType.value( Collection ) ); 199 PERF_LOG( "Loaded collection plugins" ) 200 201 PERF_LOG( "Loading service plugins" ) 202 ServicePluginManager::instance()->setFactories( m_factoriesByType.value( Service ) ); 203 PERF_LOG( "Loaded service plugins" ) 204 205 PERF_LOG( "Loading importer plugins" ) 206 StatSyncing::Controller *controller = Amarok::Components::statSyncingController(); 207 if( controller ) 208 controller->setFactories( m_factoriesByType.value( Importer ) ); 209 PERF_LOG( "Loaded importer plugins" ) 210 211 // init all new factories 212 // do this after they were added to the sub-manager so that they 213 // have a chance to connect to signals 214 // 215 // we need to init by type and the storages need to go first 216 for( const auto &factory : m_factoriesByType[ Storage ] ) 217 factory->init(); 218 for( const auto &factory : m_factoriesByType[ Collection ] ) 219 factory->init(); 220 for( const auto &factory : m_factoriesByType[ Service ] ) 221 factory->init(); 222 for( const auto &factory : m_factoriesByType[ Importer ] ) 223 factory->init(); 224 } 225 226 227 bool isPluginEnabled(const KPluginMetaData & plugin) const228Plugins::PluginManager::isPluginEnabled( const KPluginMetaData &plugin ) const 229 { 230 // mysql storage and collection are vital. They need to be loaded always 231 232 auto raw = plugin.rawData(); 233 int version = raw.value( "X-KDE-Amarok-framework-version" ).toInt(); 234 int rank = raw.value( "X-KDE-Amarok-rank" ).toInt(); 235 236 if( version != s_pluginFrameworkVersion ) 237 { 238 warning() << "Plugin" << plugin.pluginId() << "has frameworks version" << version 239 << ". Version" << s_pluginFrameworkVersion << "is required"; 240 return false; 241 } 242 243 if( rank == 0 ) 244 { 245 warning() << "Plugin" << plugin.pluginId() << "has rank 0"; 246 return false; 247 } 248 249 auto vital = raw.value( QStringLiteral( "X-KDE-Amarok-vital" ) ); 250 251 if( !vital.isUndefined()) 252 { 253 if( vital.toBool() || vital.toString().toLower() == "true" ) 254 { 255 debug() << "Plugin" << plugin.pluginId() << "is vital"; 256 return true; 257 } 258 } 259 260 KPluginInfo info = KPluginInfo( plugin ); 261 info.setConfig( Amarok::config( "Plugins" ) ); 262 info.load(); 263 264 return info.isPluginEnabled(); 265 } 266 267 268 QSharedPointer<Plugins::PluginFactory> createFactory(const KPluginMetaData & pluginInfo)269Plugins::PluginManager::createFactory( const KPluginMetaData &pluginInfo ) 270 { 271 debug() << "Creating factory for plugin:" << pluginInfo.pluginId(); 272 273 // check if we already created this factory 274 // note: old factories are not deleted. 275 // We can't very well just destroy a factory being 276 // currently used. 277 const QString name = pluginInfo.pluginId(); 278 279 if( m_factoryCreated.contains( name ) ) 280 return m_factoryCreated.value( name ); 281 282 QPluginLoader loader( pluginInfo.fileName() ); 283 auto pointer = qobject_cast<PluginFactory*>( loader.instance() ); 284 auto pluginFactory = QSharedPointer<Plugins::PluginFactory>( pointer ); 285 286 if( !pluginFactory ) 287 { 288 warning() << QString( "Failed to get factory '%1' from QPluginLoader: %2" ) 289 .arg( name, loader.errorString() ); 290 return QSharedPointer<Plugins::PluginFactory>(); 291 } 292 293 m_factoryCreated[ name ] = pluginFactory; 294 return pluginFactory; 295 } 296 297 298 QVector<KPluginMetaData> findPlugins()299Plugins::PluginManager::findPlugins() 300 { 301 QVector<KPluginMetaData> plugins; 302 for( const auto &location : QCoreApplication::libraryPaths() ) 303 plugins << KPluginLoader::findPlugins( location, [] ( const KPluginMetaData &metadata ) 304 { return metadata.serviceTypes().contains( QStringLiteral( "Amarok/Plugin" ) ); } ); 305 306 for( const auto &plugin : plugins ) 307 { 308 bool enabled = isPluginEnabled( plugin ); 309 debug() << "found plugin:" << plugin.pluginId() 310 << "enabled:" << enabled; 311 } 312 debug() << plugins.count() << "plugins in total"; 313 314 return plugins; 315 } 316 317 int pluginFrameworkVersion()318Plugins::PluginManager::pluginFrameworkVersion() 319 { 320 return s_pluginFrameworkVersion; 321 } 322 323