pluginmanager.cpp 11.1 KB
/* ****************************************************************************
 * Copyright 2019 Open Systems Development BV                                 *
 *                                                                            *
 * Permission is hereby granted, free of charge, to any person obtaining a    *
 * copy of this software and associated documentation files (the "Software"), *
 * to deal in the Software without restriction, including without limitation  *
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,   *
 * and/or sell copies of the Software, and to permit persons to whom the      *
 * Software is furnished to do so, subject to the following conditions:       *
 *                                                                            *
 * The above copyright notice and this permission notice shall be included in *
 * all copies or substantial portions of the Software.                        *
 *                                                                            *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    *
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    *
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        *
 * DEALINGS IN THE SOFTWARE.                                                  *
 * ***************************************************************************/
#include "pluginmanager.h"

#include <QCoreApplication>
#include <QFileInfo>
#include <memory>

#include "log.h"
#include "dcxmlconfig.h"
#include "iplugin.h"

#include <QtDebug>

namespace osdev  {
namespace components {

using namespace osdev::caelus;

std::unique_ptr<PluginManager> PluginManager::s_instance( nullptr );


PluginManager& PluginManager::Instance()
{
    if ( nullptr == s_instance )
    {
        s_instance = std::unique_ptr<PluginManager>( new PluginManager() );
    }

    return *s_instance;
}

bool PluginManager::addLibrary( const QString& pluginName )
{
    bool bResult = false;

    if( m_pluginHash.contains( pluginName ) )
    {
        bResult = false;
    }
    else
    {
        Plugin* l_plugin = new Plugin( pluginName );
        if( nullptr != l_plugin )
        {
            m_pluginHash.insert( pluginName, l_plugin );
            bResult = true;

            LogInfo( m_className, QString( "Library : %1 was added to the library list." ).arg( pluginName ) )
        }
    }
    return bResult;
}

bool PluginManager::loadPlugin(const QString& pluginName)
{
    bool bStatus = false;

    LogInfo( m_className, QString( "Load plugin %1" ).arg( pluginName ) )

    QPluginLoader loader( pluginName );
    QObject *plugin = loader.instance();
    if ( plugin )
    {
        LogInfo( m_className, "plugin was loaded..." )
        IPlugin *iPlugin = qobject_cast<IPlugin*>( plugin );
        if ( iPlugin )
        {
            LogInfo( m_className, QString( "%1 implements the IPlugin interface.." ).arg( pluginName ) )

            // Call initialization function on plugin
            iPlugin->initialise();

            // Get the path of the just loaded plugin
            QString pluginPath = QFileInfo(loader.fileName()).path();

            LogInfo( m_className, QString("Plugin name : %1").arg( iPlugin->getName() ) )
            LogInfo( m_className, QString("Plugin uuid : %1").arg( iPlugin->getId().toString() ) )
            LogInfo( m_className, QString("Plugin description : %1").arg( iPlugin->getDescription() ) )
            LogInfo( m_className, QString("Plugin path : %1").arg( pluginPath ) )

            // Set the created id to the pluginHash.
            Plugin *ptrPlugin = m_pluginHash.value( pluginName, nullptr );
            if ( ptrPlugin )
            {   // Retrieve the correct pointer and adjust the values.
                ptrPlugin->setName( iPlugin->getName() );
                ptrPlugin->setFileName( pluginName );
                ptrPlugin->setUuid( iPlugin->getId() );
                ptrPlugin->setPath( pluginPath );
                ptrPlugin->setPlugin( plugin );
            }
            else
            {   // The plugin just loaded doesn't exist. Create it and add it to the hash.
                ptrPlugin = new Plugin( iPlugin->getName(), pluginName, pluginPath, QString(), iPlugin->getId(), plugin );
                m_pluginHash.insert(iPlugin->getName(), ptrPlugin);
            }
            // Load successfull
            bStatus = true;

        }
        else
        {
            LogError( m_className, QString( "Ignoring plugin %1 because it does not support the IPlugin interface." ).arg( pluginName ) )
        }
    }
    else
    {
        LogError( m_className, QString( "Loading of plugin %1 failed....." ).arg( pluginName ) )
        LogError( m_className, QString( "PluginLoader reported : %1" ).arg( loader.errorString() ) )
        LogError( m_className, QString( "LibraryPaths : %1 " ).arg( QCoreApplication::libraryPaths().join( "\n" ) ) )
    }
    return bStatus;
}

bool PluginManager::loadPlugins(const QStringList& pluginNames)
{
    bool bStatus = false;

    LogDebug( m_className, QString("Adding %1 to the Hash.").arg( pluginNames.join(" ") ) )
    buildLibraryList();

    LogDebug( m_className, QString("Looking for plugins in : %1").arg( QCoreApplication::libraryPaths().join(":") ) )
    for( const QString& pluginName : pluginNames )
    {
        bStatus &= loadPlugin( pluginName );
    }

    return bStatus;
}

bool PluginManager::loadPlugins()
{
    bool bStatus = false;

    auto l_pluginNames = m_pluginHash.keys();
    for( const QString& l_pluginName : l_pluginNames )
    {
        bStatus &= loadPlugin( l_pluginName );
    }

    return bStatus;
}

bool PluginManager::unloadPlugin( const QString& pluginName )
{
    bool bStatus = false;

    QPluginLoader loader( pluginName );
    if ( loader.unload() )
    {
        LogInfo(m_className, QString("Plugin %1 successfully unloaded.").arg(pluginName))
        LogInfo(m_className, QString("Removing Plugin %1 form the hashtable.").arg(pluginName))
        Plugin *ptr_plugin = m_pluginHash.take( pluginName );
        delete ptr_plugin;
        bStatus = true;
    }
    else
    {
        LogError( m_className, QString("Unloading of plugin %1 failed.").arg(pluginName) )
        LogError( m_className, QString("Last PluginError : %1").arg(loader.errorString() ) )
    }
    return bStatus;
}

void PluginManager::addLibraryPaths(const QStringList& paths)
{
    for( const QString& libPath : paths )
    {
        QCoreApplication::addLibraryPath( libPath );
    }
}

void PluginManager::addLibraryPath(const QString& path)
{
    QCoreApplication::addLibraryPath( path );
}

QObject* PluginManager::getPlugin(const QUuid &id) const
{
    QObject *l_plugin = nullptr;

    for(auto it = m_pluginHash.begin(); it != m_pluginHash.end(); ++it)
    {
        if ( id == it.value()->uuid() )
        {
            l_plugin = it.value()->plugin();
        }
    }

    return l_plugin;
}

QList<QObject*> PluginManager::getPlugins(const QString& i_interface) const
{
    QList<QObject*> l_pObjectList;

    for(auto it = m_pluginHash.begin(); it != m_pluginHash.end(); ++it )
    {
        QObject *l_plugin = it.value()->plugin();
        if( l_plugin && l_plugin->qt_metacast( i_interface.toUtf8().data() ) )
        {
            // We've found a plugin with this interface
            l_pObjectList.push_back( l_plugin );
        }
    }

    return l_pObjectList;
}

PluginManager::PluginManager()
    : m_pluginHash()
    , m_libraryList()
    , m_className("PluginManager :: ")
{
    QCoreApplication::addLibraryPath( "." );
}

void PluginManager::buildLibraryList()
{
    // Get the config object and get the plugins and librarypaths...
    DCXmlConfig& l_config = DCXmlConfig::Instance();

    // Get all the plugins and create their representing objects..
    QStringList l_plugins = l_config.getPlugins();
    for(const QString& pluginName : l_plugins)
    {
        Plugin* plugin = new Plugin( pluginName );
        if (nullptr != plugin)
        {
            m_pluginHash.insert(pluginName, plugin);
            qDebug() << m_pluginHash;
        }
    }
}

QString PluginManager::getLibrary( const QString& pluginName ) const
{
    /// @TODO this should find the actual file name corresponding to the plugin. QFileInfo should be constructed with the actual filename.
    /// We are looking for that name so this is not working as intended.
    LogDebug( m_className, QString( "Plugin Name looking for : %1" ).arg( pluginName ) )
    for( const QString& library : m_pluginHash.keys() )
    {
        LogDebug( m_className, QString( "Library = %1" ).arg( library ) )
        QFileInfo info( library );
        LogDebug( m_className, QString( "Library base of : %1" ).arg( info.baseName() ) )
        if ( pluginName == info.baseName() )
        {   // First found can be returned
            return library;
        }
    }
    // Nothing found
    return QString();
}

bool PluginManager::isLoaded( const QString& pluginName ) const
{
    return m_pluginHash.contains( pluginName );
}

QUuid PluginManager::getPluginId( const QString& pluginName ) const
{
    if( isLoaded( pluginName ) )
    {
        return m_pluginHash.value( pluginName )->uuid();
    }
    return QUuid();
}

QUuid PluginManager::getPluginIdByInterfaceId( const QString &interface_id )
{
    for(auto it = m_pluginHash.begin(); it != m_pluginHash.end(); ++it )
    {
        QObject *l_plugin = it.value()->plugin();
        if( l_plugin && l_plugin->qt_metacast( interface_id.toUtf8().data() ) )
        {
            // We've found a plugin with this interface
            IPlugin *iPlugin = qobject_cast<IPlugin*>( it.value()->plugin() );
            if( iPlugin )
            {
                return iPlugin->getId();
            }
            else
                LogInfo( "[PluginManager::getPluginIdByInterfaceId]", QString( "No plugin found with interface : " + interface_id ) );
        }
    }
    return QUuid();
}

QString PluginManager::getPluginNameByUuid( const QUuid &uuid )
{
    for( const auto& key : m_pluginHash.keys() )
    {
        Plugin *ptr_plugin = m_pluginHash.value( key, nullptr );
        if( nullptr != ptr_plugin )
        {
            if( ptr_plugin->uuid() == uuid )
            {
                return ptr_plugin->name();
            }
        }
    }
    return QString();
}

QUuid PluginManager::getPluginUuidByName( const QString &name )
{
    for( const auto& key : m_pluginHash.keys() )
    {
        Plugin *ptr_plugin = m_pluginHash.value( key, nullptr );
        if( nullptr != ptr_plugin )
        {
            if( ptr_plugin->name() == name )
            {
                return ptr_plugin->uuid();
            }
        }
    }
    return QUuid();
}

void PluginManager::slotConfigChanged( const QString& fileName )
{
    LogInfo(m_className,
            QString( "Configurationfile %1 was changed. Reread the configuration and plugins." ).arg( fileName ) )
}

} // End namespace components
} // End namespace osdev