// ==========================================================================
// Author: Yee Hsu
// Date: 6/11/2011
//
// Desc: A Configuration Manager that uses Boost shared object library.
// This API allows user to read/write into a shared resource
// accessible by any external processes. It is mainly used for
// configuration settings but may also be used as a means for IPC
// (Inter-Process Communication). The API also allows a callback
// to any process when a setting has been changed.
// ==========================================================================
#include "ConfigFile.h"
#include "ConfigManager.h"
#include "ConfigThread.h"
#include <boost/lexical_cast.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid_generators.hpp>
//#include <boost/interprocess/sync/interprocess_condition.hpp>
//#include <boost/interprocess/mapped_region.hpp>
using namespace boost::interprocess;
namespace bip = boost::interprocess;
namespace __ET__
{
ConfigManager::ConfigManager()
: m_bInitialized(false)
, m_self_name("")
, m_psm_map()
, m_cb_context(NULL)
, CallBackFunc(NULL)
, m_ipc_mutex(NULL)
, m_cb_config()
, m_pthr(NULL)
, m_ptime(NULL)
{
// Initialize in constructor
Init();
}
ConfigManager::~ConfigManager()
{
// Shutdown in destructor
Shutdown();
}
void ConfigManager::PurgeSharedMemory()
{
// delete all queues by looking at the list
try
{
managed_shared_memory smp(open_only, SHARED_MEM_LIST);
managed_shared_memory::const_named_iterator iter = smp.named_begin();
while (iter != smp.named_end())
{
const managed_shared_memory::char_type *name = iter->name();
if (name) { shared_memory_object::remove(name); }
++iter;
}
}
catch (bip::interprocess_exception& ex){
const char* ignore = ex.what();
}
catch (std::exception& ex)
{
ET_ERROR("Error - PurgeSharedMemory(Queue) : %s\n", ex.what());
}
// delete all segments
try
{
shared_memory_object::remove(SHARED_MEM_LIST);
shared_memory_object::remove(SHARED_MEM_DATA);
shared_memory_object::remove(SHARED_MEM_GLOB);
}
catch (std::exception& ex)
{
ET_ERROR("Error - PurgeSharedMemory(Segment) : %s\n", ex.what());
}
}
ConfigManager& ConfigManager::Instance()
{
static ConfigManager _instance;
return _instance;
}
bool ConfigManager::SetSharedMemory( const String& segment )
{
try
{
m_psm_map[segment] = new managed_shared_memory(open_or_create, segment.c_str(), SHARED_MEM_SIZE);
ET_ASSERT(m_psm_map[segment]);
return bool (m_psm_map[segment] != NULL);
}
catch (std::exception& ex)
{
ET_ERROR("Error - SetSharedMemory : %s\n", ex.what());
}
return false;
}
bool ConfigManager::Init()
{
if (m_bInitialized) return true;
if (!IsReady())
{
ConfigFile::Instance().Init();
// create all our shared memory segments
if (!SetSharedMemory(SHARED_MEM_GLOB))
return false;
if (!SetSharedMemory(SHARED_MEM_DATA))
return false;
if (!SetSharedMemory(SHARED_MEM_LIST))
return false;
if (!SetSharedMemory(GenerateOrGetSelfUUID()))
return false;
// create or find our shared mutex
try
{
m_ipc_mutex = m_psm_map[SHARED_MEM_GLOB]->find_or_construct<interprocess_mutex>("_ipc_mutex")();
ET_ASSERT(m_ipc_mutex);
if (!m_ipc_mutex)
return false;
}
catch (std::exception& ex)
{
ET_ERROR("Error - Init : %s\n", ex.what());
return false;
}
// create timer thread to keep checking shared mutex
if (!m_ptime)
{
m_ptime = m_tTimer.CreateThread(new ConfigManagerTimer(this));
m_tTimer.StartAll();
}
// lets add ourself to the shared process list
try
{
SharedMemScopedLock lock(*m_ipc_mutex);
m_psm_map[SHARED_MEM_LIST]->destroy<shared_string>(GenerateOrGetSelfUUID().c_str());
m_psm_map[SHARED_MEM_LIST]->construct<shared_string>(GenerateOrGetSelfUUID().c_str())(GenerateOrGetSelfUUID().c_str(), m_psm_map[SHARED_MEM_LIST]->get_segment_manager());
}
catch (std::exception& ex)
{
ET_ERROR("Error - Init : %s\n", ex.what());
// return false;
}
m_bInitialized = true;
}
return true;
}
bool ConfigManager::IsReady() const
{
return m_bInitialized;
}
bool ConfigManager::RegisterCallback( ConfigManagerCallback callback /*= NULL*/, void* context /*= NULL*/ )
{
if (IsReady() && !CallBackFunc)
{
CallBackFunc=callback;
m_cb_context=context;
if (!m_pthr)
{
m_pthr = m_tWorker.CreateThread(new ConfigManagerWorker(this));
m_tWorker.StartAll();
}
return true;
}
return false;
}
void ConfigManager::DelSharedMemory( const String& segment )
{
if (m_psm_map[segment])
{
delete m_psm_map[segment];
m_psm_map[segment] = NULL;
}
}
void ConfigManager::Shutdown()
{
if (IsReady())
{
m_tWorker.StopAll();
m_tTimer.StopAll();
try
{
SharedMemScopedLock lock(*m_ipc_mutex);
m_psm_map[SHARED_MEM_LIST]->destroy<shared_string>(GenerateOrGetSelfUUID().c_str()); // remove self from list
}
catch (std::exception& ex)
{
ET_ERROR("Error - Shutdown : %s\n", ex.what());
}
try
{
shared_memory_object::remove(GenerateOrGetSelfUUID().c_str()); // remove self queue
DelSharedMemory(GenerateOrGetSelfUUID().c_str());
DelSharedMemory(SHARED_MEM_LIST);
DelSharedMemory(SHARED_MEM_DATA);
DelSharedMemory(SHARED_MEM_GLOB);
}
catch (std::exception& ex)
{
ET_ERROR("Error - Shutdown : %s\n", ex.what());
}
ConfigFile::Instance().Shutdown();
m_bInitialized = false;
}
}
bool ConfigManager::HasSettings(const String& sKey)
{
return HasSettings(SHARED_MEM_DATA, sKey);
}
bool ConfigManager::GetSettings( KeyList& Keys )
{
return GetSettings(SHARED_MEM_DATA, Keys);
}
bool ConfigManager::GetSettings(const String& sKey, String& sValue)
{
if (GetSettings(SHARED_MEM_DATA, sKey, sValue))
return true;
else
return ConfigFile::Instance().GetSettings(sKey, sValue);
}
bool ConfigManager::DelSettings(const String& sKey)
{
if (DelSettings(SHARED_MEM_DATA, sKey))
{
SetSettings(sKey, CONFIG_DEL);
return true;
}
return false;
}
bool ConfigManager::SetSettings(const String& sKey, const String& sValue)
{
if (ConfigFile::Instance().HasSettings(sKey, sValue))
return false;
if (SetSettings(SHARED_MEM_DATA, sKey, sValue))
{
SetSettings(sKey, CONFIG_SET);
return true;
}
return false;
}
bool ConfigManager::HasSettings(const String& segment, const String& sKey)
{
if (!IsReady())
return false;
try
{
//SharedMemScopedLock lock(*m_ipc_mutex);
std::pair<shared_string*, size_t> sp = m_psm_map[segment]->find<shared_string>(sKey.c_str());
if (sp.first)
return true;
}
catch (std::exception& ex)
{
ET_ERROR("Error - HasSettings : %s\n", ex.what());
}
return false;
}
bool ConfigManager::GetSettings(const String& segment, KeyList& Keys)
{
if (!IsReady())
return false;
try
{
//SharedMemScopedLock lock(*m_ipc_mutex);
managed_shared_memory::const_named_iterator iter = m_psm_map[segment]->named_begin();
Keys.clear();
while (iter != m_psm_map[segment]->named_end())
{
const managed_shared_memory::char_type *name = iter->name();
//std::size_t name_len = iter->name_length();
//const void *value = iter->value();
Keys.push_back(name);
++iter;
}
return true;
}
catch (std::exception& ex)
{
ET_ERROR("Error - GetSettings : %s\n", ex.what());
}
return false;
}
bool ConfigManager::GetSettings(const String& segment, MapList& objs)
{
if (!IsReady())
return false;
KeyList keys;
if (GetSettings(segment, keys))
{
objs.clear();
for (KeyList::iterator iter = keys.begin(); iter != keys.end(); ++iter)
{
ConfigObject obj;
obj.sKey = iter->c_str();
if (GetSettings(segment, obj.sKey, obj.sValue))
{
objs.push_back(obj);
}
}
return true;
}
return false;
}
bool ConfigManager::GetSettings(const String& segment, SelfMemMap& objs)
{
if (!IsReady())
return false;
KeyList keys;
if (GetSettings(segment, keys))
{
objs.clear();
for (KeyList::iterator iter = keys.begin(); iter != keys.end(); ++iter)
{
int nValue;
if (GetSettings(segment, iter->c_str(), nValue))
{
objs[iter->c_str()] = nValue;
}
}
return true;
}
return false;
}
bool ConfigManager::DelSettings(const String& segment, const String& sKey)
{
if (!IsReady())
return false;
try
{
SharedMemScopedLock lock(*m_ipc_mutex);
m_psm_map[segment]->destroy<shared_string>(sKey.c_str());
return ConfigFile::Instance().DelSettings(sKey);
}
catch (std::exception& ex)
{
ET_ERROR("Error - DelSettings : %s\n", ex.what());
}
return false;
}
bool ConfigManager::DelSelfKeys( const String& sKey )
{
if (!IsReady())
return false;
try
{
//SharedMemScopedLock lock(*m_ipc_mutex);
managed_shared_memory smp(open_only, GenerateOrGetSelfUUID().c_str());
smp.destroy<int>(sKey.c_str());
return true;
}
catch (std::exception& ex)
{
ET_ERROR("Error - DelSelfKeys : %s\n", ex.what());
}
return false;
}
bool ConfigManager::SetSettings(const String& segment, const String& sKey, const String& sValue)
{
if (!IsReady())
return false;
try
{
SharedMemScopedLock lock(*m_ipc_mutex);
m_psm_map[segment]->destroy<shared_string>(sKey.c_str());
if (m_psm_map[segment]->construct<shared_string>(sKey.c_str())(sValue.c_str(), m_psm_map[segment]->get_segment_manager()))
{
ConfigFile::Instance().SetSettings(sKey, sValue);
return true;
}
}
catch (std::exception& ex)
{
ET_ERROR("Error - SetSettings : %s\n", ex.what());
}
return false;
}
bool ConfigManager::SetSettings(const String& sKey, const int& nOpCode)
{
if (!IsReady())
return false;
KeyList keys;
if (GetSettings(SHARED_MEM_LIST, keys))
{
for (KeyList::iterator iter = keys.begin(); iter != keys.end(); ++iter)
{
String other_uuid = iter->c_str();
if (other_uuid != GenerateOrGetSelfUUID())
{
SetSettings(other_uuid, sKey, nOpCode);
}
}
}
return true;
}
bool ConfigManager::SetSettings(const String& segment, const String& sKey, const int& nOpCode)
{
if (!IsReady())
return false;
try
{
//SharedMemScopedLock lock(*m_ipc_mutex);
managed_shared_memory smp(open_only, segment.c_str());
smp.destroy<int>(sKey.c_str());
smp.construct<int>(sKey.c_str())(nOpCode);
}
catch (std::exception& ex)
{
ET_ERROR("Error - SetSettings : %s\n", ex.what());
return false;
}
return true;
}
bool ConfigManager::GetSettings(const String& segment, const String& sKey, String& sValue)
{
if (!IsReady())
return false;
try
{
sValue = "";
//SharedMemScopedLock lock(*m_ipc_mutex);
std::pair<shared_string*, size_t> sp = m_psm_map[segment]->find<shared_string>(sKey.c_str());
if (sp.first)
{
sValue = sp.first->c_str();
return true;
}
}
catch (std::exception& ex)
{
ET_ERROR("Error - GetSettings : %s\n", ex.what());
}
return false;
}
bool ConfigManager::GetSettings(const String& segment, const String& sKey, int &nValue)
{
if (!IsReady())
return false;
try
{
//SharedMemScopedLock lock(*m_ipc_mutex);
std::pair<int*, size_t> sp = m_psm_map[segment]->find<int>(sKey.c_str());
if (sp.first)
{
nValue = *(sp.first);
return true;
}
}
catch (std::exception& ex)
{
ET_ERROR("Error - GetSettings : %s\n", ex.what());
}
return false;
}
long ConfigManager::NumSettings( const String& segment )
{
if (!IsReady())
return false;
try
{
//SharedMemScopedLock lock(*m_ipc_mutex);
return m_psm_map[segment]->get_num_named_objects();
}
catch (std::exception& ex)
{
ET_ERROR("Error - NumSettings : %s\n", ex.what());
}
return 0;
}
void ConfigManager::NotifyCallback( const String& sKey, const int& nValue )
{
if (!IsReady())
return;
if (!CallBackFunc)
return;
try
{
m_cb_config.sKey = sKey;
m_cb_config.sValue = "";
m_cb_config.nOpCode = nValue;
if (m_cb_config.nOpCode == CONFIG_SET)
{
GetSettings(sKey, m_cb_config.sValue);
}
CallBackFunc(m_cb_config, m_cb_context);
}
catch (std::exception& ex)
{
ET_ERROR("Error - NotifyCallback : %s\n", ex.what());
}
}
__ET__::String ConfigManager::GenerateOrGetSelfUUID()
{
if (m_self_name.empty())
{
boost::uuids::uuid uuid = boost::uuids::random_generator()();
const std::string suuid = boost::lexical_cast<std::string>(uuid);
m_self_name = "__ET___APP_" + suuid;
}
return m_self_name;
}
void ConfigManager::UnlockInfiniteBlockedSharedMutex()
{
if (m_ipc_mutex)
{
try
{
boost::system_time timeout=boost::get_system_time() +
boost::posix_time::milliseconds(10 * 1000);
if (!m_ipc_mutex->timed_lock(timeout))
{
ET_TRACE("Aquiring Shared Mutex Failed: Unlocking!");
}
}
catch (std::exception& ex)
{
ET_ERROR("Error - ConfigManager:UnlockInfiniteBlockedSharedMutex : %s\n", ex.what());
}
m_ipc_mutex->unlock();
}
}
}