Commit 6cfa328d authored by Emmanuel Milou's avatar Emmanuel Milou

Add the dynamic loading for the plugin framework; integate unit tests

parent 26393bbe
# Global variables
src=$(top_srcdir)
sflcodecdir=$(libdir)/sflphone/codecs
sflplugindir=$(libdir)/sflphone/plugins
PJSIP_LIBS = -lpjnath-sfl -lpjsua-sfl -lpjsip-sfl -lpjmedia-sfl -lpjsip-simple-sfl -lpjsip-ua-sfl -lpjmedia-codec-sfl -lpjlib-util-sfl -lpj-sfl
......@@ -16,5 +17,6 @@ AM_CPPFLAGS = \
@SIP_CFLAGS@ \
@DBUSCPP_CFLAGS@ \
-DCODECS_DIR=\""$(sflcodecdir)"\" \
-DPLUGINS_DIR=\""$(sflplugindir)"\" \
-DENABLE_TRACE
......@@ -87,6 +87,7 @@ libsflphone_la_LIBADD = \
./audio/libaudio.la \
./dbus/libdbus.la \
./config/libconfig.la \
./plug-in/libplugin.la \
$(IAX_LIBS)
libsflphone_la_SOURCES =
include ../../globals.mak
noinst_LTLIBRARIES = libplugin.la
libplugin_la_SOURCES = \
pluginmanager.cpp
plugin.cpp
pluginmanager.cpp \
plugin.h
#include "plugin.h"
Plugin::Plugin( const std::string &filename )
::sflphone::Plugin::Plugin( const std::string &filename UNUSED )
{
//TODO IMPLEMENT
}
Plugin::Plugin( const Plugin &plugin )
::sflphone::Plugin::Plugin( const Plugin &plugin UNUSED )
{
//TODO IMPLEMENT
}
Plugin::~Plugin()
::sflphone::Plugin::~Plugin()
{
//TODO IMPLEMENT
}
int Plugin::getCoreVersion( void )
int ::sflphone::Plugin::getCoreVersion( void ) const
{
//TODO IMPLEMENT
return 1;
}
void Plugin::registerPlugin( PluginManager & )
void ::sflphone::Plugin::registerPlugin( PluginManager & )
{
//TODO IMPLEMENT
}
......@@ -3,6 +3,8 @@
#include <string>
#include "global.h"
/*
* @file plugin.h
* @brief Define a plugin object
......@@ -15,21 +17,21 @@ class PluginManager;
class Plugin {
public:
Plugin( const std::string &filename );
Plugin( const Plugin &plugin );
~Plugin();
Plugin( const std::string &name );
//Plugin( const Plugin &plugin );
virtual ~Plugin() {}
public:
/**
* Return the minimal core version required so that the plugin could work
* @return int The version required
*/
int getCoreVersion() const;
virtual int getCoreVersion() const = 0;
/**
* Register the plugin to the plugin manager
*/
void registerPlugin( PluginManager & );
virtual void registerPlugin( PluginManager & ) = 0;
private:
Plugin &operator =(const Plugin &plugin);
......
#include <dirent.h>
#include <dlfcn.h>
#include "pluginmanager.h"
void ::sflphone::PluginManager::loadPlugins( const std::string &path )
::sflphone::PluginManager::PluginManager():_loadedPlugins()
{
_instance = this;
}
::sflphone::PluginManager::~PluginManager()
{
_instance = 0;
}
int ::sflphone::PluginManager::loadPlugins( const std::string &path )
{
std::string pluginDir, current;
::sflphone::Plugin *plugin;
DIR *dir;
dirent *dirStruct;
int result=0;
const std::string pDir = "..";
const std::string cDir = ".";
/* The directory in which plugins are dropped. Default: /usr/lib/sflphone/plugins/ */
( path == "" )? pluginDir = std::string(PLUGINS_DIR).append("/"):pluginDir = path;
_debug("Loading plugins from %s...\n", pluginDir.c_str());
dir = opendir( pluginDir.c_str() );
/* Test if the directory exists or is readable */
if( dir ){
/* Read the directory */
while( (dirStruct=readdir(dir)) ){
/* Get the name of the current item in the directory */
current = dirStruct->d_name;
/* Test if the current item is not the parent or the current directory */
if( current != pDir && current != cDir ){
loadDynamicLibrary( current );
result++;
}
}
}
/* Close the directory */
closedir( dir );
return result;
}
::sflphone::Plugin* ::sflphone::PluginManager::isPluginLoaded( const std::string &name )
{
//TODO IMPLEMENT
if(_loadedPlugins.empty()) return NULL;
/* Use an iterator on the loaded plugins map */
pluginMap::iterator iter;
iter = _loadedPlugins.begin();
while( iter != _loadedPlugins.end() ) {
if ( iter->first == name ) {
/* Return the associated plugin */
return iter->second;
}
iter++;
}
/* If we are here, it means that the plugin we were looking for has not been loaded */
return NULL;
}
void* ::sflphone::PluginManager::loadDynamicLibrary( const std::string& filename ) {
void *pluginHandlePtr = NULL;
const char *error;
_debug("Loading dynamic library %s\n", filename.c_str());
#if defined(Q_OS_UNIX)
/* Load the library */
pluginHandlePtr = dlopen( filename.c_str(), RTLD_LAZY );
if( !pluginHandlePtr ) {
error = dlerror();
_debug("Error while opening plug-in: %s\n", error);
}
dlerror();
#endif
return pluginHandlePtr;
}
void ::sflphone::PluginManager::unloadDynamicLibrary( void * pluginHandlePtr ) {
dlclose( pluginHandlePtr );
dlerror();
}
::sflphone::PluginManager* ::sflphone::PluginManager::_instance = 0;
......@@ -7,6 +7,7 @@
*/
#include "plugin.h"
#include "global.h"
#include <map>
#include <string>
......@@ -16,17 +17,55 @@ namespace sflphone {
class PluginManager {
public:
/**
* Default constructor
*/
PluginManager();
/**
* Destructor
*/
~PluginManager();
/**
* Returns the unique instance of the plugin manager
*/
static PluginManager* instance();
/**
* Load all the plugins found in a specific directory
* @param path The absolute path to the directory
* @return int The number of items loaded
*/
int loadPlugins( const std::string &path = "" );
/**
* Check if a plugin has been already loaded
* @param name The name of the plugin looked for
* @return Plugin* The pointer on the plugin or NULL if not found
*/
void loadPlugins( const std::string &path );
Plugin* isPluginLoaded( const std::string &name );
private:
/* Map of plugins associated by their string name */
typedef std::map<std::string, ::sflphone::Plugin> pluginMap;
/**
* Load a unix dynamic/shared library
* @param filename The path to the dynamic/shared library
* @return void* A pointer on it
*/
void * loadDynamicLibrary( const std::string &filename );
/**
* Unload a unix dynamic/shared library
* @param pluginHandleptr The pointer on the loaded plugin
*/
void unloadDynamicLibrary( void * pluginHandlePtr );
/* Map of plugins associated by their string name */
typedef std::map<std::string, ::sflphone::Plugin*> pluginMap;
pluginMap _loadedPlugins;
/* The unique static instance */
static PluginManager* _instance;
};
}
......
include ../globals.mak
bin_PROGRAMS = configurationTester
bin_PROGRAMS = configurationTester pluginmanagerTester
OBJECT_FILES= \
../src/sflphoned-managerimpl.o \
......@@ -16,6 +16,7 @@ OBJECT_FILES= \
../src/sflphoned-iaxaccount.o \
../src/sflphoned-eventthread.o \
../src/sflphoned-useragent.o \
../src/plug-in/pluginmanager.o \
../src/sflphoned-samplerateconverter.o
configurationTester_SOURCES = \
......@@ -23,6 +24,11 @@ configurationTester_SOURCES = \
configurationTest.h \
TestMain.cpp
pluginmanagerTester_SOURCES = \
pluginmanagerTest.h \
pluginmanagerTest.cpp \
TestMain.cpp
configurationTester_LDADD = \
../src/libsflphone.la \
$(SFLPHONE_LIBS) $(ZEROCONFLIB) $(LIB_DNSSD) $(IAX_LIBS) \
......@@ -36,4 +42,18 @@ configurationTester_LDADD = \
@SAMPLERATE_LIBS@ \
$(PJSIP_LIBS) \
$(OBJECT_FILES)
pluginmanagerTester_LDADD = \
../src/libsflphone.la \
$(SFLPHONE_LIBS) $(ZEROCONFLIB) $(LIB_DNSSD) $(IAX_LIBS) \
@ALSA_LIBS@ \
@PULSEAUDIO_LIBS@ \
@CPPUNIT_LIBS@ \
@CCEXT2_LIBS@ \
@CCGNU2_LIBS@ \
@CCRTP_LIBS@ \
@DBUSCPP_LIBS@ \
@SAMPLERATE_LIBS@ \
$(PJSIP_LIBS) \
$(OBJECT_FILES)
/*
* Copyright (C) 2009 Savoir-Faire Linux inc.
* Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <sstream>
#include "pluginmanagerTest.h"
using std::cout;
using std::endl;
void PluginManagerTest::setUp(){
// Instanciate the plugin manager object
_pm = new ::sflphone::PluginManager();
}
void PluginManagerTest::testLoadPluginDirectory(){
_pm->loadPlugins();
}
void PluginManagerTest::testNonloadedPlugin(){
CPPUNIT_ASSERT( _pm->isPluginLoaded("test") == NULL );
}
void PluginManagerTest::tearDown(){
// Delete the plugin manager object
delete _pm; _pm=NULL;
}
/*
* Copyright (C) 2009 Savoir-Faire Linux inc.
* Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
// Cppunit import
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestCase.h>
#include <cppunit/TestSuite.h>
#include <assert.h>
// Application import
#include "plug-in/pluginmanager.h"
/*
* @file pluginManagerTest.cpp
* @brief Regroups unitary tests related to the plugin manager.
*/
#ifndef _PLUGINMANAGER_TEST_
#define _PLUGINMANAGER_TEST_
class PluginManagerTest : public CppUnit::TestCase {
/*
* Use cppunit library macros to add unit test the factory
*/
CPPUNIT_TEST_SUITE( PluginManagerTest );
CPPUNIT_TEST( testLoadPluginDirectory );
CPPUNIT_TEST( testNonloadedPlugin );
CPPUNIT_TEST_SUITE_END();
public:
PluginManagerTest() : CppUnit::TestCase("Plugin Manager Tests") {}
/*
* Code factoring - Common resources can be initialized here.
* This method is called by unitcpp before each test
*/
void setUp();
/*
* Code factoring - Common resources can be released here.
* This method is called by unitcpp after each test
*/
inline void tearDown();
void testLoadPluginDirectory();
void testNonloadedPlugin();
private:
::sflphone::PluginManager *_pm;
};
/* Register our test module */
CPPUNIT_TEST_SUITE_REGISTRATION( PluginManagerTest );
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment