1、 负责插件的加载,检测,初始化。
2、 负责服务的注册。
3、 负责服务的调用。
4、 服务的管理。
运行环境:ubuntu20
代码用到的组件:log4cpp
cmakelists
# 设置release版本还是debug版本
cmake_minimum_required(VERSION 3.16)
if(${CMAKE_BUILD_TYPE} MATCHES "Release")
MESSAGE(STATUS "Release版本")
SET(BuildType "Release")
else()
SET(BuildType "Debug")
MESSAGE(STATUS "Debug版本")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -W -Wall -Wextra -g")
include_directories(
/usr/local/include/orocos/
/usr/local/include/
/usr/include
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
#设置lib库目录
SET(RELEASE_DIR ${PROJECT_SOURCE_DIR}/release)
# debug和release版本目录不一样
#设置生成的so动态库最后输出的路径
SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR})
ADD_COMPILE_OPTIONS(-fPIC)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
# AUX_SOURCE_DIRECTORY(. DIR_LIB_SRCS)
# 默认生成静态库链接库Dir1
#ADD_LIBRARY (Dir1 ${DIR_LIB_SRCS})
# SHARED生成动态库
# ADD_LIBRARY (Test SHARED ${DIR_LIB_SRCS})
ADD_LIBRARY (Test3 SHARED test.h test.cpp plugin.h)
target_link_libraries(Test3 dl liborocos-log4cpp.so pthread)
plugin.conf(用于配置log4cpp)
log4cpp.rootCategory=DEBUG, root
log4cpp.rootCategory.Mgr=, pluginMgr
log4cpp.appender.root=org.apache.log4cpp.ConsoleAppender
log4cpp.appender.root.layout=org.apache.log4cpp.PatternLayout
log4cpp.appender.root.layout.ConversionPattern=[%d{%Y-%m-%d %H:%M:%S.%l} - %p] (%c): %m%n
log4cpp.appender.Mgr=org.apache.log4cpp.ConsoleAppender
log4cpp.appender.Mgr.layout=org.apache.log4cpp.PatternLayout
plugin.h
//plugin.h
#ifndef __PLUGIN_H__
#define __PLUGIN_H__
#include
#include
#include
#include
using namespace std;
class IPlugin
{
public:
IPlugin() = default;
virtual ~IPlugin() = default;
IPlugin(const IPlugin&) = delete;
IPlugin& operator=(const IPlugin&) = delete;
public:
//获取支持的插件方法
virtual const list<string>& getSupportCommandList() const {return supportCommandList_; }
public:
virtual string getVersionString() const = 0;
//! \brief 获取业务名称
//! \return
virtual string getPluginName() const = 0;
enum pluginState_type
{
idle_,
starting_,
started_,
stopping_,
stopped_,
excepting_,
};
//! 获取插件的运行状态。
//! \return
virtual pluginState_type getPluginState() const = 0;
//! 获取插件的运行状态的解释
//! \param state
//! \return
static string getPluginStateMessage(const pluginState_type& state)
{
string msg = "no msg.";
switch (state)
{
case pluginState_type::idle_:
msg = "plugin is on idle statement.";
break;
case pluginState_type::starting_:
msg = "plugin is on starting statement.";
break;
case pluginState_type::started_:
msg = "plugin is on started statement.";
break;
case pluginState_type::stopping_:
msg = "plugin is on stopping statement.";
break;
case pluginState_type::stopped_:
msg = "plugin is on stopped statement.";
break;
case pluginState_type::excepting_:
msg = "plugin is on excepting statement.";
break;
}
return msg;
}
//! \brief 执行业务的主要接口方法
//! \param msg 由外部程序发送的通讯协议(比如json报文)
//! \return 错误信息
virtual void exec(const string& msg) = 0;
//! \brief 业务停止运行
//! \return
virtual void stop() { return ; }
//! \brief 释放动态库资源
//! \return 错误信息
virtual void release() = 0;
protected:
list<string> supportCommandList_;
};
#endif
test.h
//test.h
#ifndef __TRIANGLE_H__
#define __TRIANGLE_H__
#include "plugin.h"
#include "log4cpp/Category.hh"
#include "log4cpp/PropertyConfigurator.hh"
#include "log4cpp/NDC.hh"
#include
class Test3 : public IPlugin
{
public:
Test3();
virtual string getVersionString() const;
//! \brief 获取业务名称
//! \return
virtual string getPluginName() const;
public:
//! 获取插件的运行状态。
//! \return
virtual pluginState_type getPluginState() const;
//! \brief 执行业务的主要接口方法
//! \param msg 由外部程序发送的通讯协议(比如json报文)
//! \return 错误信息
virtual void exec(const string& msg);
//! \brief 业务停止运行
//! \return
virtual void stop();
//! \brief 释放动态库资源
//! \return 错误信息
virtual void release();
protected:
list<string> supportCommandList_;
log4cpp::Category *m_log;;
};
#endif /* __TRIANGLE_H__ */
test.cpp
//test.cpp
#include "test.h"
extern "C"
{
void * getInstance()
{
return new Test3;
}
}
Test3::Test3()
{
try
{
log4cpp::PropertyConfigurator::configure("../plugin.conf");
}
catch (log4cpp::ConfigureFailure &f)
{
std::cout << "Configure Problem " << f.what() << std::endl;
exit(1);
}
m_log = &log4cpp::Category::getInstance(std::string("Test3"));
}
Test3::pluginState_type Test3::getPluginState() const
{
return Test3::starting_;
}
string Test3::getVersionString() const
{
return "3.0";
}
string Test3::getPluginName() const
{
return "Test3";
}
void Test3::exec(const string& msg)
{
m_log->debug("execute command %s",msg.c_str());
}
void Test3::stop()
{
m_log->debug("program stop");
}
void Test3::release()
{
m_log->debug("release plugram");
}
编译后即可在…/release中生成libTest3.so共享库。
cmake_minimum_required(VERSION 3.5)
project(plugin LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -W -Wall -Wextra -g")
include_directories(
/usr/local/include/orocos/
/usr/local/include/
/usr/include
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(plugin main.cpp plugin.h pluginMgr.cpp pluginMgr.h)
target_link_libraries(plugin dl liborocos-log4cpp.so pthread)
plugin.config
log4cpp.rootCategory=DEBUG, root
log4cpp.rootCategory.Mgr=, pluginMgr
log4cpp.appender.root=org.apache.log4cpp.ConsoleAppender
log4cpp.appender.root.layout=org.apache.log4cpp.PatternLayout
log4cpp.appender.root.layout.ConversionPattern=[%d{%Y-%m-%d %H:%M:%S.%l} - %p] (%c): %m%n
log4cpp.appender.Mgr=org.apache.log4cpp.ConsoleAppender
log4cpp.appender.Mgr.layout=org.apache.log4cpp.PatternLayout
pluginMgr.h
//pluginMgr.h
#ifndef __PLUGINMGR_H__
#define __PLUGINMGR_H__
#include
#include
#include
#include
#include"plugin.h"
#include "log4cpp/Category.hh"
#include "log4cpp/PropertyConfigurator.hh"
#include "log4cpp/NDC.hh"
#define workPath "../lib/"
#define MIN_FILE_LENGTH 3
#define LOGGING_ERROR printf
#define LOGGING_WARN printf
#define LOGGING_DEBUG printf
#define PROJECT_NAME "PluginTest"
#define PROJECT_VERSION "1.0"
class PluginManager
{
private:
PluginManager();
public:
static PluginManager& getInstance();
~PluginManager();
static const char* getLibraryName();
static const char* getLibraryVersion();
public:
void loadAll();
void unloadAll();
bool isloadedFromPath(string soFileName);
bool loadFromPath(const string& libPath);
void *resolve(void *handle,string className);
bool unloadFromPath(const string& name);
list<string> getNames() const ;
IPlugin* get(const string& name);
private:
unordered_map<string, IPlugin*> m_plugins;
unordered_map<string, pair<string,void *>> m_libs;
log4cpp::Category *m_log;
};
#endif
pluginMgr.cpp
//pluginMgr.cpp
#include"pluginMgr.h"
typedef void *(*instance)();
PluginManager::PluginManager()
{
try
{
log4cpp::PropertyConfigurator::configure("../plugin.conf");
}
catch (log4cpp::ConfigureFailure &f)
{
std::cout << "Configure Problem " << f.what() << std::endl;
exit(1);
}
m_log = &log4cpp::Category::getInstance(std::string("pluginMgr"));
}
PluginManager &PluginManager::getInstance()
{
static PluginManager w;
return w;
}
PluginManager::~PluginManager()
{
unloadAll();
}
void PluginManager::loadAll()
{
struct dirent *entry;
DIR *dp;
dp = opendir(workPath);
if (dp == NULL) {
m_log->error("opendir error,work path : %s\n",workPath);
return ;
}
while((entry = readdir(dp)))
{
string files = entry->d_name;
if(files.length() > MIN_FILE_LENGTH && files.substr(files.length()-3,3) == ".so")
{
string libPath = workPath+files;
if(isloadedFromPath(libPath))
{
m_log->warn("%s had been loaded before.", libPath.c_str());
continue;
}
loadFromPath(libPath);
}
}
closedir(dp);
}
void PluginManager::unloadAll()
{
for(auto iter = m_plugins.begin();iter!=m_plugins.end();iter++)
{
iter->second->release();
}
for(auto iter = m_libs.begin();iter!=m_libs.end();iter++)
{
m_log->debug(iter->first.c_str());
pair<string,void*>plugin = iter->second;
void *handle = plugin.second;
dlclose(handle);
}
m_plugins.clear();
m_libs.clear();
m_log->debug("%s unloaded all",PROJECT_NAME);
}
bool PluginManager::loadFromPath(const string& libPath)
{
void *handle = dlopen(libPath.c_str(), RTLD_LAZY);
if(handle)
{
instance getPluginInstance = (instance)resolve(handle,"getInstance");
if(getPluginInstance)
{
auto plugin = (IPlugin *)getPluginInstance();
if(plugin)
{
auto pluginName = plugin->getPluginName();
if (m_plugins.count(pluginName))
{
m_log->warn("%s repeated loading.", pluginName.c_str());
unloadFromPath(libPath);
return false;
}
m_plugins[pluginName] = plugin;
m_libs[libPath] = pair<string,void *>(pluginName,handle);
m_log->debug("load plugin name : %s , version: %s", plugin->getPluginName().c_str(),
plugin->getVersionString().c_str());
}
else
{
m_log->error("%s object create failed.", libPath.c_str());
unloadFromPath(libPath);
return false;
}
}
else
{
m_log->error("%s cannot find symbol.", libPath.c_str());
unloadFromPath(libPath);
return false;
}
}
else
{
m_log->error("%s load failed.",libPath.c_str());
return false;
}
return true;
}
void* PluginManager::resolve(void *handle,string className)
{
return dlsym(handle, className.c_str());
}
bool PluginManager::isloadedFromPath(string soFileName)
{
auto iter = m_libs.find(soFileName);
if(iter==m_libs.end()) return false;
return true;
}
bool PluginManager::unloadFromPath(const string& name)
{
pair<string,void *> plugin = m_libs[name];
void *handle = plugin.second;
string pluginName = plugin.first;
IPlugin *temp = m_plugins[pluginName];
if(!handle || !temp)
{
m_log->error("%s : unload errer !,dont have handle.",name.c_str());
return false;
}
temp->release();
dlclose(handle);
for(auto iter = m_plugins.begin();iter!=m_plugins.end();iter++)
{
if(iter->first == pluginName)
{
m_plugins.erase(iter);
}
}
for(auto iter = m_libs.begin();iter!=m_libs.end();iter++)
{
if(iter->first == name)
{
m_libs.erase(iter);
}
}
m_log->debug("%s unload successfully . ",name.c_str());
return true;
}
IPlugin *PluginManager::get(const string &name)
{
if (m_plugins.find(name)!=m_plugins.end())
{
m_log->debug("%s get successfully.", name.c_str());
return m_plugins[name];
}
m_log->error("%s is not found.", name.c_str());
return nullptr;
}
list<string> PluginManager::getNames() const
{
list<string> res;
for(auto iter = m_plugins.begin();iter!=m_plugins.end();iter++)
{
res.push_back(iter->first);
}
return res;
}
const char *PluginManager::getLibraryName()
{
return PROJECT_NAME;
}
const char *PluginManager::getLibraryVersion()
{
return PROJECT_VERSION;
}
plugin.h
//plugin.h
#ifndef __PLUGIN_H__
#define __PLUGIN_H__
#include
#include
#include
#include
using namespace std;
#ifndef SERVICEPROJECT_IPLUGIN_H
#define SERVICEPROJECT_IPLUGIN_H
class IPlugin
{
public:
IPlugin() = default;
virtual ~IPlugin() = default;
IPlugin(const IPlugin&) = delete;
IPlugin& operator=(const IPlugin&) = delete;
public:
//获取支持的插件方法
virtual const list<string>& getSupportCommandList() const {return supportCommandList_; }
public:
virtual string getVersionString() const = 0;
//! \brief 获取业务名称
//! \return
virtual string getPluginName() const = 0;
enum pluginState_type
{
idle_,
starting_,
started_,
stopping_,
stopped_,
excepting_,
};
//! 获取插件的运行状态。
//! \return
virtual pluginState_type getPluginState() const = 0;
//! 获取插件的运行状态的解释
//! \param state
//! \return
static string getPluginStateMessage(const pluginState_type& state)
{
string msg = "no msg.";
switch (state)
{
case pluginState_type::idle_:
msg = "plugin is on idle statement.";
break;
case pluginState_type::starting_:
msg = "plugin is on starting statement.";
break;
case pluginState_type::started_:
msg = "plugin is on started statement.";
break;
case pluginState_type::stopping_:
msg = "plugin is on stopping statement.";
break;
case pluginState_type::stopped_:
msg = "plugin is on stopped statement.";
break;
case pluginState_type::excepting_:
msg = "plugin is on excepting statement.";
break;
}
return msg;
}
//! \brief 执行业务的主要接口方法
//! \param msg 由外部程序发送的通讯协议(比如json报文)
//! \return 错误信息
virtual void exec(const string& msg) = 0;
//! \brief 业务停止运行
//! \return
virtual void stop() { return ; }
//! \brief 释放动态库资源
//! \return 错误信息
virtual void release() = 0;
protected:
list<string> supportCommandList_;
};
#endif // SERVICEPROJECT_IPLUGIN_H
#endif
main.cpp
//main.cpp
#include
#include"plugin.h"
#include"pluginMgr.h"
#include
using namespace std;
int main()
{
PluginManager::getInstance().loadAll();
IPlugin *plugin = PluginManager::getInstance().get("Test1");
plugin->exec("Test1");
plugin->stop();
plugin = PluginManager::getInstance().get("Test2");
plugin->exec("Test2");
plugin->stop();
plugin = PluginManager::getInstance().get("Test3");
plugin->exec("Test3");
plugin->stop();
PluginManager::getInstance().unloadAll();
PluginManager::getInstance().loadFromPath("../lib/libTest1.so");
plugin = PluginManager::getInstance().get("Test1");
plugin->exec("Test1");
PluginManager::getInstance().unloadFromPath("../lib/libTest1.so");
}