File indexing completed on 2024-04-21 04:47:53
0001 /**************************************************************************************** 0002 * Copyright (c) 2004-2013 Mark Kretschmann <kretschmann@kde.org> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify it under * 0005 * the terms of the GNU General Public License as published by the Free Software * 0006 * Foundation; either version 2 of the License, or (at your option) any later * 0007 * version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0010 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0011 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0012 * * 0013 * You should have received a copy of the GNU General Public License along with * 0014 * this program. If not, see <http://www.gnu.org/licenses/>. * 0015 ****************************************************************************************/ 0016 0017 #define DEBUG_PREFIX "PluginManager" 0018 0019 #include "PluginManager.h" 0020 0021 #include <core/support/Amarok.h> 0022 #include <core/support/Components.h> 0023 #include <core/support/Debug.h> 0024 #include <core-impl/collections/support/CollectionManager.h> 0025 #include <core-impl/storage/StorageManager.h> 0026 #include <services/ServiceBase.h> 0027 #include <services/ServicePluginManager.h> 0028 #include <statsyncing/Controller.h> 0029 #include <statsyncing/ProviderFactory.h> 0030 #include <storage/StorageFactory.h> 0031 0032 #include <KLocalizedString> 0033 #include <KMessageBox> 0034 #include <KPluginLoader> 0035 0036 #include <QGuiApplication> 0037 0038 0039 /** Defines the used plugin version number. 0040 * 0041 * This must match the desktop files. 0042 */ 0043 const int Plugins::PluginManager::s_pluginFrameworkVersion = 74; 0044 Plugins::PluginManager* Plugins::PluginManager::s_instance = nullptr; 0045 0046 Plugins::PluginManager* 0047 Plugins::PluginManager::instance() 0048 { 0049 return s_instance ? s_instance : new PluginManager(); 0050 } 0051 0052 void 0053 Plugins::PluginManager::destroy() 0054 { 0055 if( s_instance ) 0056 { 0057 delete s_instance; 0058 s_instance = nullptr; 0059 } 0060 } 0061 0062 Plugins::PluginManager::PluginManager( QObject *parent ) 0063 : QObject( parent ) 0064 { 0065 DEBUG_BLOCK 0066 setObjectName( "PluginManager" ); 0067 s_instance = this; 0068 0069 PERF_LOG( "Initialising Plugin Manager" ) 0070 init(); 0071 PERF_LOG( "Initialised Plugin Manager" ) 0072 } 0073 0074 Plugins::PluginManager::~PluginManager() 0075 { 0076 // tell the managers to get rid of their current factories 0077 QList<QSharedPointer<Plugins::PluginFactory> > emptyFactories; 0078 0079 StatSyncing::Controller *controller = Amarok::Components::statSyncingController(); 0080 if( controller ) 0081 controller->setFactories( emptyFactories ); 0082 ServicePluginManager::instance()->setFactories( emptyFactories ); 0083 CollectionManager::instance()->setFactories( emptyFactories ); 0084 StorageManager::instance()->setFactories( emptyFactories ); 0085 } 0086 0087 void 0088 Plugins::PluginManager::init() 0089 { 0090 checkPluginEnabledStates(); 0091 } 0092 0093 KPluginInfo::List 0094 Plugins::PluginManager::plugins( Type type ) const 0095 { 0096 KPluginInfo::List infos; 0097 0098 for( const auto &pluginInfo : m_pluginsByType.value( type ) ) 0099 { 0100 auto info = KPluginInfo( pluginInfo ); 0101 info.setConfig( Amarok::config( "Plugins" ) ); 0102 infos << info; 0103 } 0104 0105 return infos; 0106 } 0107 0108 QVector<KPluginMetaData> 0109 Plugins::PluginManager::enabledPlugins(Plugins::PluginManager::Type type) const 0110 { 0111 QVector<KPluginMetaData> enabledList; 0112 0113 for( const auto &plugin : m_pluginsByType.value( type ) ) 0114 { 0115 if( isPluginEnabled( plugin ) ) 0116 enabledList << plugin; 0117 } 0118 0119 return enabledList; 0120 } 0121 0122 QList<QSharedPointer<Plugins::PluginFactory> > 0123 Plugins::PluginManager::factories( Type type ) const 0124 { 0125 return m_factoriesByType.value( type ); 0126 } 0127 0128 void 0129 Plugins::PluginManager::checkPluginEnabledStates() 0130 { 0131 DEBUG_BLOCK 0132 0133 // re-create all the member infos. 0134 m_plugins.clear(); 0135 m_pluginsByType.clear(); 0136 m_factoriesByType.clear(); 0137 0138 m_plugins = findPlugins(); // reload all the plugins plus their enabled state 0139 0140 if( m_plugins.isEmpty() ) // exit if no plugins are found 0141 { 0142 if( qobject_cast<QGuiApplication*>( qApp ) ) 0143 { 0144 KMessageBox::error( nullptr, i18n( "Amarok could not find any plugins. This indicates an installation problem." ) ); 0145 } 0146 else 0147 { 0148 warning() << "Amarok could not find any plugins. Bailing out."; 0149 } 0150 // don't use QApplication::exit, as the eventloop may not have started yet 0151 std::exit( EXIT_SUCCESS ); 0152 } 0153 0154 // sort the plugin infos by type 0155 for( const auto &pluginInfo : m_plugins ) 0156 { 0157 // create the factories and sort them by type 0158 auto factory = createFactory( pluginInfo ); 0159 0160 if( factory ) 0161 { 0162 Type type; 0163 0164 if( qobject_cast<StorageFactory*>( factory ) ) 0165 type = Storage; 0166 else if( qobject_cast<Collections::CollectionFactory*>( factory ) ) 0167 type = Collection; 0168 else if( qobject_cast<ServiceFactory*>( factory ) ) 0169 type = Service; 0170 else if( qobject_cast<StatSyncing::ProviderFactory*>( factory ) ) 0171 type = Importer; 0172 else 0173 { 0174 warning() << pluginInfo.name() << "has unknown category"; 0175 warning() << pluginInfo.rawData().keys(); 0176 continue; 0177 } 0178 0179 m_pluginsByType[ type ] << pluginInfo; 0180 0181 if( isPluginEnabled( pluginInfo ) ) 0182 m_factoriesByType[ type ] << factory; 0183 } 0184 else 0185 warning() << pluginInfo.name() << "could not create factory"; 0186 } 0187 0188 // the setFactories functions should: 0189 // - filter out factories not useful (e.g. services when setting collections) 0190 // - handle the new list of factories, disabling old ones and enabling new ones. 0191 0192 0193 PERF_LOG( "Loading storage plugins" ) 0194 StorageManager::instance()->setFactories( m_factoriesByType.value( Storage ) ); 0195 PERF_LOG( "Loaded storage plugins" ) 0196 0197 PERF_LOG( "Loading collection plugins" ) 0198 CollectionManager::instance()->setFactories( m_factoriesByType.value( Collection ) ); 0199 PERF_LOG( "Loaded collection plugins" ) 0200 0201 PERF_LOG( "Loading service plugins" ) 0202 ServicePluginManager::instance()->setFactories( m_factoriesByType.value( Service ) ); 0203 PERF_LOG( "Loaded service plugins" ) 0204 0205 PERF_LOG( "Loading importer plugins" ) 0206 StatSyncing::Controller *controller = Amarok::Components::statSyncingController(); 0207 if( controller ) 0208 controller->setFactories( m_factoriesByType.value( Importer ) ); 0209 PERF_LOG( "Loaded importer plugins" ) 0210 0211 // init all new factories 0212 // do this after they were added to the sub-manager so that they 0213 // have a chance to connect to signals 0214 // 0215 // we need to init by type and the storages need to go first 0216 for( const auto &factory : m_factoriesByType[ Storage ] ) 0217 factory->init(); 0218 for( const auto &factory : m_factoriesByType[ Collection ] ) 0219 factory->init(); 0220 for( const auto &factory : m_factoriesByType[ Service ] ) 0221 factory->init(); 0222 for( const auto &factory : m_factoriesByType[ Importer ] ) 0223 factory->init(); 0224 } 0225 0226 0227 bool 0228 Plugins::PluginManager::isPluginEnabled( const KPluginMetaData &plugin ) const 0229 { 0230 // mysql storage and collection are vital. They need to be loaded always 0231 0232 auto raw = plugin.rawData(); 0233 int version = raw.value( "X-KDE-Amarok-framework-version" ).toInt(); 0234 int rank = raw.value( "X-KDE-Amarok-rank" ).toInt(); 0235 0236 if( version != s_pluginFrameworkVersion ) 0237 { 0238 warning() << "Plugin" << plugin.pluginId() << "has frameworks version" << version 0239 << ". Version" << s_pluginFrameworkVersion << "is required"; 0240 return false; 0241 } 0242 0243 if( rank == 0 ) 0244 { 0245 warning() << "Plugin" << plugin.pluginId() << "has rank 0"; 0246 return false; 0247 } 0248 0249 auto vital = raw.value( QStringLiteral( "X-KDE-Amarok-vital" ) ); 0250 0251 if( !vital.isUndefined()) 0252 { 0253 if( vital.toBool() || vital.toString().toLower() == "true" ) 0254 { 0255 debug() << "Plugin" << plugin.pluginId() << "is vital"; 0256 return true; 0257 } 0258 } 0259 0260 KPluginInfo info = KPluginInfo( plugin ); 0261 info.setConfig( Amarok::config( "Plugins" ) ); 0262 info.load(); 0263 0264 return info.isPluginEnabled(); 0265 } 0266 0267 0268 QSharedPointer<Plugins::PluginFactory> 0269 Plugins::PluginManager::createFactory( const KPluginMetaData &pluginInfo ) 0270 { 0271 debug() << "Creating factory for plugin:" << pluginInfo.pluginId(); 0272 0273 // check if we already created this factory 0274 // note: old factories are not deleted. 0275 // We can't very well just destroy a factory being 0276 // currently used. 0277 const QString name = pluginInfo.pluginId(); 0278 0279 if( m_factoryCreated.contains( name ) ) 0280 return m_factoryCreated.value( name ); 0281 0282 QPluginLoader loader( pluginInfo.fileName() ); 0283 auto pointer = qobject_cast<PluginFactory*>( loader.instance() ); 0284 auto pluginFactory = QSharedPointer<Plugins::PluginFactory>( pointer ); 0285 0286 if( !pluginFactory ) 0287 { 0288 warning() << QString( "Failed to get factory '%1' from QPluginLoader: %2" ) 0289 .arg( name, loader.errorString() ); 0290 return QSharedPointer<Plugins::PluginFactory>(); 0291 } 0292 0293 m_factoryCreated[ name ] = pluginFactory; 0294 return pluginFactory; 0295 } 0296 0297 0298 QVector<KPluginMetaData> 0299 Plugins::PluginManager::findPlugins() 0300 { 0301 QVector<KPluginMetaData> plugins; 0302 for( const auto &location : QCoreApplication::libraryPaths() ) 0303 plugins << KPluginLoader::findPlugins( location, [] ( const KPluginMetaData &metadata ) 0304 { return metadata.serviceTypes().contains( QStringLiteral( "Amarok/Plugin" ) ); } ); 0305 0306 for( const auto &plugin : plugins ) 0307 { 0308 bool enabled = isPluginEnabled( plugin ); 0309 debug() << "found plugin:" << plugin.pluginId() 0310 << "enabled:" << enabled; 0311 } 0312 debug() << plugins.count() << "plugins in total"; 0313 0314 return plugins; 0315 } 0316 0317 int 0318 Plugins::PluginManager::pluginFrameworkVersion() 0319 { 0320 return s_pluginFrameworkVersion; 0321 } 0322