diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a69bc0..b1f926e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,7 +57,7 @@ if ("${DINDEXER_CONFIG_FILE}" STREQUAL "") endif() message(STATUS "Config file set to \"${DINDEXER_CONFIG_FILE}\"") -find_package(Boost 1.53.0 REQUIRED COMPONENTS program_options filesystem) +find_package(Boost 1.53.0 REQUIRED COMPONENTS program_options filesystem system) find_package(PostgreSQL 8.3 REQUIRED) find_package(YamlCpp 0.5.1 REQUIRED) import_libpqtypes_project("${PostgreSQL_INCLUDE_DIRS}" "-O3 ${march_flag}") diff --git a/dindexer.yml b/dindexer.yml index 38242d4..0f1330f 100644 --- a/dindexer.yml +++ b/dindexer.yml @@ -7,3 +7,4 @@ postgresql_settings: dbname: dindexer port: 5432 address: 200.100.200.100 +backend_paths: path to your build_dir/src/backend diff --git a/include/dindexer-common/split_tags.hpp b/include/dindexer-common/split_tags.hpp index 149cc25..3005751 100644 --- a/include/dindexer-common/split_tags.hpp +++ b/include/dindexer-common/split_tags.hpp @@ -24,6 +24,7 @@ #include "helpers/compatibility.h" namespace dinlib { + std::vector split_and_trim ( const std::string& parList, char parSeparator ) a_pure; std::vector split_tags ( const std::string& parCommaSeparatedList ) a_pure; } //namespace dinlib diff --git a/src/backends/CMakeLists.txt b/src/backends/CMakeLists.txt index 60e0a31..f091a99 100644 --- a/src/backends/CMakeLists.txt +++ b/src/backends/CMakeLists.txt @@ -1,5 +1,14 @@ project(${bare_name}-backend CXX) +set(BACKEND_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") +function(ln_backend backend_name) + add_custom_command(TARGET "${backend_name}" POST_BUILD + COMMAND ${CMAKE_COMMAND} -E create_symlink $ ${BACKEND_BINARY_DIR}/$ + DEPENDS ${BACKEND_BINARY_DIR}/$ + COMMENT "Creating symlink to ${backend_name}" + ) +endfunction() + add_library(${PROJECT_NAME} STATIC backend_loader.cpp ) @@ -26,6 +35,5 @@ target_link_libraries(${PROJECT_NAME} add_subdirectory(postgresql) add_subdirectory(redis) - add_dependencies(${PROJECT_NAME} ${bare_name}-backend-postgresql) add_dependencies(${PROJECT_NAME} ${bare_name}-backend-redis) diff --git a/src/backends/backend_loader.cpp b/src/backends/backend_loader.cpp index 3aa8547..05b5909 100644 --- a/src/backends/backend_loader.cpp +++ b/src/backends/backend_loader.cpp @@ -24,6 +24,7 @@ #include #include #include +#include namespace dindb { namespace { @@ -34,9 +35,22 @@ namespace dindb { assert(parSOHandle); assert(parConfig); - auto create = reinterpret_cast(dlsym(parSOHandle, "dindexer_create_backend")); - auto destroy = reinterpret_cast(dlsym(parSOHandle, "dindexer_destroy_backend")); + const char* const fun_name_create = "dindexer_create_backend"; + const char* const fun_name_destroy = "dindexer_destroy_backend"; + auto create = reinterpret_cast(dlsym(parSOHandle, fun_name_create)); + auto destroy = reinterpret_cast(dlsym(parSOHandle, fun_name_destroy)); + + if (not create) { + std::ostringstream oss; + oss << "Unable to find function " << fun_name_create; + throw std::runtime_error(oss.str()); + } + if (not destroy) { + std::ostringstream oss; + oss << "Unable to find function " << fun_name_destroy; + throw std::runtime_error(oss.str()); + } return BackendPtr(create(parConfig), destroy); } @@ -65,7 +79,10 @@ namespace dindb { using SoHandle = std::unique_ptr; auto handle = SoHandle(dlopen(parSOPath.c_str(), RTLD_LAZY), &dlclose); - return backend_name(handle.get()); + if (handle) + return backend_name(handle.get()); + else + return std::string(); } BackendPlugin::BackendPlugin() : diff --git a/src/backends/postgresql/CMakeLists.txt b/src/backends/postgresql/CMakeLists.txt index 89b8680..486d5b0 100644 --- a/src/backends/postgresql/CMakeLists.txt +++ b/src/backends/postgresql/CMakeLists.txt @@ -23,3 +23,4 @@ install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib/static ) +ln_backend(${PROJECT_NAME}) diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 23eeb7d..95b6b45 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -16,33 +16,40 @@ */ #include "dindexer-common/settings.hpp" +#include "dindexer-common/split_tags.hpp" #include "dindexerConfig.h" #include #include #include #include #include +#include +#include namespace dinlib { namespace { std::string expand ( const char* parString ); - std::string find_plugin_by_name ( const std::string& parName ); + std::string find_plugin_by_name ( const std::vector& parSearchPaths, const std::string& parName ); void throw_if_plugin_failed ( const dindb::BackendPlugin& parPlugin, const std::string& parPluginPath, const std::string& parIntendedName ); } //unnamed namespace void load_settings (const std::string& parPath, dinlib::Settings& parOut, bool parExpand) { const std::string path = (parExpand ? expand(parPath.c_str()) : parPath); + std::string search_paths; auto settings = YAML::LoadFile(path); - if (not settings["backend_name"]) { + if (not settings["backend_name"]) throw std::runtime_error("No backend_name given in the config file"); - } + if (settings["backend_paths"]) + search_paths += ":" + settings["backend_paths"].as(); parOut.backend_name = settings["backend_name"].as(); const std::string backend_settings_section = parOut.backend_name + "_settings"; if (settings[backend_settings_section]) { auto settings_node = settings[backend_settings_section]; - const std::string plugin_path = find_plugin_by_name(parOut.backend_name); + const std::string plugin_path = find_plugin_by_name(split_and_trim(search_paths, ':'), parOut.backend_name); + if (plugin_path.empty()) + throw std::runtime_error(std::string("Unable to find any suitable plugin with the specified name \"") + parOut.backend_name + "\""); parOut.backend_plugin = dindb::BackendPlugin(plugin_path, &settings_node); throw_if_plugin_failed(parOut.backend_plugin, plugin_path, parOut.backend_name); } @@ -61,14 +68,24 @@ namespace dinlib { return oss.str(); } - std::string find_plugin_by_name (const std::string& parName) { - //assert(false); //not implemented - //TODO: write a proper implementation - std::string path = ACTIONS_SEARCH_PATH; - path += "/backends/postgresql/libdindexer-backend-postgresql.so"; + std::string find_plugin_by_name (const std::vector& parSearchPaths, const std::string& parName) { + using boost::filesystem::path; + using boost::filesystem::is_directory; + using boost::filesystem::directory_iterator; + using boost::filesystem::directory_entry; + using boost::make_iterator_range; - assert(dindb::backend_name(path) == parName); - return path; + for (auto search_path : parSearchPaths) { + const std::string search_path_cpy(search_path.begin(), search_path.end()); + path curr_path(search_path_cpy); + auto listing = make_iterator_range(directory_iterator(curr_path), directory_iterator()); + for (const directory_entry& entry : listing) { + auto current_full_path = entry.path().string(); + if (not is_directory(entry) and dindb::backend_name(current_full_path) == parName) + return current_full_path; + } + } + return std::string(); } void throw_if_plugin_failed (const dindb::BackendPlugin& parPlugin, const std::string& parPluginPath, const std::string& parIntendedName) { diff --git a/src/common/split_tags.cpp b/src/common/split_tags.cpp index 4ba7275..931a8d5 100644 --- a/src/common/split_tags.cpp +++ b/src/common/split_tags.cpp @@ -26,6 +26,10 @@ namespace dinlib { std::vector split_tags (const std::string& parCommaSeparatedList) { + return split_and_trim(parCommaSeparatedList, ','); + } + + std::vector split_and_trim (const std::string& parList, char parSeparator) { using OutRange = boost::iterator_range; using boost::token_finder; using boost::adaptors::transformed; @@ -42,7 +46,7 @@ namespace dinlib { //http://www.boost.org/doc/libs/1_60_0/doc/html/boost/algorithm/token_finder.html //https://stackoverflow.com/questions/20781090/difference-between-boostsplit-vs-boostiter-split return boost::copy_range>( - iter_split(out_range, parCommaSeparatedList, token_finder([](char c){return ','==c;})) | + iter_split(out_range, parList, token_finder([parSeparator](char c){return parSeparator==c;})) | transformed([](const OutRange& r){return trim_copy(r);}) | transformed([](const OutRange& r){return string_ref(&*r.begin(), r.size());}) | filtered([](const string_ref& r){return not r.empty();})