/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2011 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba 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, see .
*/
#include
#include
#include
#include
#include
#include
#if !defined(WIN32)
#include
#endif
MTS_NAMESPACE_BEGIN
// -----------------------------------------------------------------------
// Abstract plugin module implementation
// -----------------------------------------------------------------------
Plugin::Plugin(const std::string &shortName, const fs::path &path)
: m_shortName(shortName), m_path(path) {
#if defined(WIN32)
m_handle = LoadLibrary(path.file_string().c_str());
if (!m_handle) {
SLog(EError, "Error while loading plugin \"%s\": %s",
m_path.file_string().c_str(), lastErrorText().c_str());
}
#else
m_handle = dlopen(path.file_string().c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!m_handle) {
SLog(EError, "Error while loading plugin \"%s\": %s",
m_path.file_string().c_str(), dlerror());
}
#endif
try {
m_getDescription = (GetDescriptionFunc) getSymbol("GetDescription");
} catch (...) {
#if defined(WIN32)
FreeLibrary(m_handle);
#else
dlclose(m_handle);
#endif
throw;
}
m_createInstance = NULL;
m_createUtility = NULL;
m_isUtility = false;
if (hasSymbol("CreateUtility")) {
m_createUtility = (CreateUtilityFunc) getSymbol("CreateUtility");
m_isUtility = true;
} else {
m_createInstance = (CreateInstanceFunc) getSymbol("CreateInstance");
}
Statistics::getInstance()->logPlugin(shortName, getDescription());
/* New classes must be registered within the class hierarchy */
Class::staticInitialization();
}
bool Plugin::hasSymbol(const std::string &sym) const {
#if defined(WIN32)
void *ptr = GetProcAddress(m_handle, sym.c_str());
#else
void *ptr = dlsym(m_handle, sym.c_str());
#endif
return ptr != NULL;
}
void *Plugin::getSymbol(const std::string &sym) {
#if defined(WIN32)
void *data = GetProcAddress(m_handle, sym.c_str());
if (!data) {
SLog(EError, "Could not resolve symbol \"%s\" in \"%s\": %s",
sym.c_str(), m_path.file_string().c_str(), lastErrorText().c_str());
}
#else
void *data = dlsym(m_handle, sym.c_str());
if (!data) {
SLog(EError, "Could not resolve symbol \"%s\" in \"%s\": %s",
sym.c_str(), m_path.file_string().c_str(), dlerror());
}
#endif
return data;
}
ConfigurableObject *Plugin::createInstance(const Properties &props) const {
return (ConfigurableObject *) m_createInstance(props);
}
Utility *Plugin::createUtility() const {
return (Utility *) m_createUtility();
}
std::string Plugin::getDescription() const {
return m_getDescription();
}
Plugin::~Plugin() {
#if defined(WIN32)
FreeLibrary(m_handle);
#else
dlclose(m_handle);
#endif
}
// -----------------------------------------------------------------------
// Plugin manager
// -----------------------------------------------------------------------
ref PluginManager::m_instance = NULL;
PluginManager::PluginManager() {
m_mutex = new Mutex();
}
PluginManager::~PluginManager() {
/* Release the memory used by plugin modules */
for (std::map::iterator it = m_plugins.begin();
it != m_plugins.end(); ++it) {
delete (*it).second;
}
}
ConfigurableObject *PluginManager::createObject(const Class *classType,
const Properties &props) {
ConfigurableObject *object;
m_mutex->lock();
try {
ensurePluginLoaded(props.getPluginName());
object = m_plugins[props.getPluginName()]->createInstance(props);
} catch (std::runtime_error &e) {
m_mutex->unlock();
throw e;
} catch (std::exception &e) {
m_mutex->unlock();
throw e;
}
m_mutex->unlock();
if (!object->getClass()->derivesFrom(classType))
Log(EError, "Type mismatch when loading plugin \"%s\": Expected "
"an instance of \"%s\"", props.getPluginName().c_str(), classType->getName().c_str());
if (object->getClass()->isAbstract())
Log(EError, "Error when loading plugin \"%s\": Identifies itself as an abstract class",
props.getPluginName().c_str());
return object;
}
ConfigurableObject *PluginManager::createObject(const Properties &props) {
ConfigurableObject *object;
m_mutex->lock();
try {
ensurePluginLoaded(props.getPluginName());
object = m_plugins[props.getPluginName()]->createInstance(props);
} catch (std::runtime_error &e) {
m_mutex->unlock();
throw e;
} catch (std::exception &e) {
m_mutex->unlock();
throw e;
}
m_mutex->unlock();
if (object->getClass()->isAbstract())
Log(EError, "Error when loading plugin \"%s\": Identifies itself as an abstract class",
props.getPluginName().c_str());
return object;
}
std::vector PluginManager::getLoadedPlugins() const {
std::vector list;
m_mutex->lock();
for (std::map::const_iterator it = m_plugins.begin();
it != m_plugins.end(); ++it) {
list.push_back((*it).first);
}
m_mutex->unlock();
return list;
}
void PluginManager::ensurePluginLoaded(const std::string &name) {
/* Plugin already loaded? */
if (m_plugins[name] != NULL)
return;
/* Build the full plugin file name */
#if defined(WIN32)
std::string shortName = std::string("plugins/") + name + std::string(".dll");
#elif defined(__OSX__)
std::string shortName = std::string("plugins/") + name + std::string(".dylib");
#else
std::string shortName = std::string("plugins/") + name + std::string(".so");
#endif
const FileResolver *resolver = Thread::getThread()->getFileResolver();
fs::path path = resolver->resolve(shortName);
if (fs::exists(path)) {
Log(EInfo, "Loading plugin \"%s\" ..", shortName.c_str());
m_plugins[name] = new Plugin(shortName, path.file_string());
return;
}
/* Plugin not found! */
Log(EError, "Plugin \"%s\" not found!", name.c_str());
}
void PluginManager::staticInitialization() {
m_instance = new PluginManager();
}
void PluginManager::staticShutdown() {
m_instance = NULL;
}
Version::Version(const std::string &versionString) {
std::vector tokens = tokenize(trim(versionString), ".");
if (tokens.size() != 3)
SLog(EError, "Unable to parse version string \"%s\"!", versionString.c_str());
char *end_ptr = NULL;
m_major = strtol(tokens[0].c_str(), &end_ptr, 10);
if (*end_ptr != '\0')
SLog(EError, "Unable to parse the major program version \"%i\"!", tokens[0].c_str());
m_minor = strtol(tokens[1].c_str(), &end_ptr, 10);
if (*end_ptr != '\0')
SLog(EError, "Unable to parse the minor program version \"%i\"!", tokens[1].c_str());
m_release = strtol(tokens[2].c_str(), &end_ptr, 10);
if (*end_ptr != '\0')
SLog(EError, "Unable to parse the release program version \"%i\"!", tokens[2].c_str());
}
std::string Version::toString() const {
return formatString("%i.%i.%i", m_major, m_minor, m_release);
}
MTS_IMPLEMENT_CLASS(PluginManager, false, Object)
MTS_NAMESPACE_END