diff options
author | Aleksandar Kanchev <kanchev@itestra.com> | 2013-07-24 09:50:18 +0200 |
---|---|---|
committer | Gerrit Code Review <qqmthk1@lpmodthk02.bmwgroup.net> | 2013-07-24 09:50:18 +0200 |
commit | 4accbb4a2c1bcefe15c7aa3e698c0ff79ed8fdac (patch) | |
tree | 9ddacb6e4068c56680186ca22b4396d8826ee803 /src | |
parent | be1761cff29995a03337319effcadb07b34cde30 (diff) | |
parent | 751e239d58f7382799561190369aa7b480dc6db3 (diff) | |
download | genivi-common-api-runtime-4accbb4a2c1bcefe15c7aa3e698c0ff79ed8fdac.tar.gz |
Merge "Introduced dynamic loading of middleware bindings and other generic libraries."
Diffstat (limited to 'src')
-rw-r--r-- | src/CommonAPI/CommonAPI.h | 1 | ||||
-rw-r--r-- | src/CommonAPI/Configuration.cpp | 166 | ||||
-rw-r--r-- | src/CommonAPI/Configuration.h | 121 | ||||
-rw-r--r-- | src/CommonAPI/Factory.h | 1 | ||||
-rw-r--r-- | src/CommonAPI/Factory.hpp | 10 | ||||
-rw-r--r-- | src/CommonAPI/MiddlewareInfo.h | 17 | ||||
-rw-r--r-- | src/CommonAPI/Runtime.cpp | 334 | ||||
-rw-r--r-- | src/CommonAPI/Runtime.h | 98 | ||||
-rw-r--r-- | src/CommonAPI/utils.h | 340 |
9 files changed, 1033 insertions, 55 deletions
diff --git a/src/CommonAPI/CommonAPI.h b/src/CommonAPI/CommonAPI.h index f72d1d2..e9beb77 100644 --- a/src/CommonAPI/CommonAPI.h +++ b/src/CommonAPI/CommonAPI.h @@ -14,6 +14,7 @@ #include "Runtime.h" #include "Factory.h" #include "AttributeExtension.h" +#include "ByteBuffer.h" #include "types.h" #undef COMMONAPI_INTERNAL_COMPILATION diff --git a/src/CommonAPI/Configuration.cpp b/src/CommonAPI/Configuration.cpp new file mode 100644 index 0000000..470015d --- /dev/null +++ b/src/CommonAPI/Configuration.cpp @@ -0,0 +1,166 @@ +/* Copyright (C) 2013 BMW Group + * Author: Manfred Bathelt (manfred.bathelt@bmw.de) + * Author: Juergen Gehring (juergen.gehring@bmw.de) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include <fstream> + +#include "Configuration.h" +#include "utils.h" + + +namespace CommonAPI { + +std::unordered_map<std::string, std::string> knownMiddlewareAliases_; +std::unordered_map<std::string, std::string> knownMiddlewarePaths_; +std::unordered_map<std::string, std::vector<std::string> > knownGeneratedPaths_; +std::vector<std::string> librarySearchPaths_; +std::string defaultBindingIdentifier_ = ""; + + +const Configuration& Configuration::getInstance() { + static Configuration* instance = NULL; + if (!instance) { + instance = new Configuration(); + instance->retrieveCommonApiConfiguration(); + } + return *instance; +} + +const std::vector<std::string>& Configuration::getLibrarySearchPaths() const { + return librarySearchPaths_; +} + +const std::string& Configuration::getMiddlewareNameForAlias(const std::string& alias) const { + auto foundMiddlewareName = knownMiddlewareAliases_.find(alias); + if (foundMiddlewareName != knownMiddlewareAliases_.end()) { + return foundMiddlewareName->second; + } else { + return alias; + } +} + +const std::string& Configuration::getMiddlewareLibraryPath(const std::string& middlewareIdentifier) const { + auto foundMiddlewarePath = knownMiddlewarePaths_.find(middlewareIdentifier); + if (foundMiddlewarePath == knownMiddlewarePaths_.end()) { + static const std::string emptyReturn = ""; + return emptyReturn; + } + return foundMiddlewarePath->second; +} + +const std::vector<std::string>& Configuration::getGenericLibraryPaths(const std::string& middlewareIdentifier) const { + const auto& generatedPathsForMiddleware = knownGeneratedPaths_.find(middlewareIdentifier); + if (generatedPathsForMiddleware != knownGeneratedPaths_.end()) { + return generatedPathsForMiddleware->second; + } + static const std::vector<std::string> emptyReturn; + return emptyReturn; +} + +const std::string& Configuration::getDefaultMiddlewareIdentifier() const { + return defaultBindingIdentifier_; +} + +void Configuration::readConfigFile(std::ifstream& addressConfigFile) { + std::string currentlyParsedBindingIdentifier = ""; + bool endFile = false; + + std::string readLine; + + while (addressConfigFile.good()) { + getline(addressConfigFile, readLine); + const size_t readLineLength = readLine.length(); + + if (strncmp(readLine.c_str(), CATEGORY_IDENTIFIER_BINDING, strlen(CATEGORY_IDENTIFIER_BINDING)) == 0 + && readLine[readLineLength - 1] == CATEGORY_ENDING) { + + std::string newBindingIdentifier = readLine.substr( + strlen(CATEGORY_IDENTIFIER_BINDING), + readLineLength - (strlen(CATEGORY_IDENTIFIER_BINDING) + 1)); + + trim(newBindingIdentifier); + if (containsOnlyAlphanumericCharacters(newBindingIdentifier)) { + currentlyParsedBindingIdentifier = newBindingIdentifier; + } + + } else if (currentlyParsedBindingIdentifier != "") { + std::vector<std::string> parameterElements = split(readLine, '='); + if (parameterElements.size() == 2) { + + if (parameterElements.at(0) == BINDING_PARAMETER_ALIAS) { + std::vector<std::string> aliases = split(parameterElements.at(1), ':'); + for (const std::string& singleAlias: aliases) { + knownMiddlewareAliases_.insert( {singleAlias, currentlyParsedBindingIdentifier}); + } + + } else if (parameterElements.at(0) == BINDING_PARAMETER_LIBPATH) { + knownMiddlewarePaths_.insert( {currentlyParsedBindingIdentifier, parameterElements.at(1)}); + + } else if (parameterElements.at(0) == BINDING_PARAMETER_GENPATH) { + std::vector<std::string> paths = split(parameterElements.at(1), ':'); + auto alreadyKnownPaths = knownGeneratedPaths_.find(currentlyParsedBindingIdentifier); + + if (alreadyKnownPaths == knownGeneratedPaths_.end()) { + const std::vector<std::string> pathSet(paths.begin(), paths.end()); + knownGeneratedPaths_.insert( {currentlyParsedBindingIdentifier, std::move(pathSet)} ); + } else { + alreadyKnownPaths->second.insert(alreadyKnownPaths->second.end(), paths.begin(), paths.end()); + } + } + + } else if (parameterElements.size() == 1) { + if (parameterElements.at(0) == BINDING_PARAMETER_DEFAULT && defaultBindingIdentifier_ == "") { + defaultBindingIdentifier_ = currentlyParsedBindingIdentifier; + } + } + } + } +} + + +void Configuration::readEnvironmentVariables() { + librarySearchPaths_ = split(COMMONAPI_STD_LIB_PATH, ':'); + + const char* environmentBindingPath = getenv(COMMONAPI_ENVIRONMENT_BINDING_PATH); + if (environmentBindingPath) { + std::vector<std::string> environmentPaths = split(environmentBindingPath, ':'); + librarySearchPaths_.insert(librarySearchPaths_.begin(), environmentPaths.begin(), environmentPaths.end()); + } +} + + +void Configuration::retrieveCommonApiConfiguration() { + readEnvironmentVariables(); + + std::string fqnOfConfigFile = getCurrentBinaryFileFQN(); + fqnOfConfigFile += COMMONAPI_CONFIG_SUFFIX; + + std::ifstream commonapiConfigFile; + commonapiConfigFile.open(fqnOfConfigFile.c_str()); + + if (commonapiConfigFile.is_open()) { + readConfigFile(commonapiConfigFile); + commonapiConfigFile.close(); + } + + commonapiConfigFile.clear(); + std::vector<std::string> splittedConfigFQN = split(fqnOfConfigFile, '/'); + std::string globalConfigFQN = COMMONAPI_GLOBAL_CONFIG_ROOT + splittedConfigFQN.at(splittedConfigFQN.size() - 1); + commonapiConfigFile.open(globalConfigFQN); + if (commonapiConfigFile.is_open()) { + readConfigFile(commonapiConfigFile); + commonapiConfigFile.close(); + } + commonapiConfigFile.clear(); + + commonapiConfigFile.open(COMMONAPI_GLOBAL_CONFIG_FQN); + if (commonapiConfigFile.is_open()) { + readConfigFile(commonapiConfigFile); + commonapiConfigFile.close(); + } +} + + +} // namespace CommonAPI diff --git a/src/CommonAPI/Configuration.h b/src/CommonAPI/Configuration.h new file mode 100644 index 0000000..554518e --- /dev/null +++ b/src/CommonAPI/Configuration.h @@ -0,0 +1,121 @@ +/* Copyright (C) 2013 BMW Group + * Author: Manfred Bathelt (manfred.bathelt@bmw.de) + * Author: Juergen Gehring (juergen.gehring@bmw.de) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef COMMONAPI_CONFIGURATION_H_ +#define COMMONAPI_CONFIGURATION_H_ + + +#include <unordered_map> +#include <vector> +#include <string> +#include <cstring> + + +namespace CommonAPI { + + +static const char COMMONAPI_CONFIG_SUFFIX[] = ".conf"; +static const char COMMONAPI_GLOBAL_CONFIG_ROOT[] = "/etc/CommonAPI/"; +static const char COMMONAPI_GLOBAL_CONFIG_FQN[] = "/etc/CommonAPI/CommonAPI.conf"; + +static const char COMMONAPI_STD_LIB_PATH[] = "/usr/lib:/usr/local/lib/"; +static const char COMMONAPI_ENVIRONMENT_BINDING_PATH[] = "COMMONAPI_BINDING_PATH"; + +static const char CATEGORY_ENDING = '}'; + +static const char CATEGORY_IDENTIFIER_BINDING[] = "{binding:"; + +static const char BINDING_PARAMETER_ALIAS[] = "alias"; +static const char BINDING_PARAMETER_LIBPATH[] = "libpath"; +static const char BINDING_PARAMETER_GENPATH[] = "genpath"; +static const char BINDING_PARAMETER_DEFAULT[] = "default"; + + +/** + * Represents the contents of all parsed CommonAPI Configuration files. + * + * For more information on how to configure CommonAPI, see attached documentation. + */ +class Configuration { + public: + /** + * Returns the instance of the Configuration. + * + * When first calling this method, all configuration files that are found are parsed and + * the values are stored within this class. + * + * @return The singleton instance of the CommonAPI Configuration. + */ + static const Configuration& getInstance(); + + Configuration(const Configuration&) = delete; + Configuration& operator=(const Configuration&) = delete; + Configuration(Configuration&&) = delete; + Configuration& operator=(Configuration&&) = delete; + + /** + * Returns the search paths on which binding specific libraries may be found. + * + * Default search paths are /usr/lib and /usr/local/lib, those two will always be returned. + * If additional search paths have been configured, those will also be returned. + * + * @return + */ + const std::vector<std::string>& getLibrarySearchPaths() const; + + /** + * Returns the actual middleware identifier for the given alias. + * + * If no such alias has been configured, the given alias itself will be returned. + * + * @return The middleware identifier or the given alias itself, if no mapping to a middleware identifier was found. + */ + const std::string& getMiddlewareNameForAlias(const std::string& alias) const; + + /** + * Returns the specified library path for the given middleware identifier. + * + * If a path to a specific library has been configured for the given middleware identifier, this path will be returned. + * If no such path has been configured, the empty string will be returned. + * + * @return The path to the middleware library, if any is known, the empty string "" otherwise. + */ + const std::string& getMiddlewareLibraryPath(const std::string& middlewareIdentifier) const; + + /** + * Returns the paths to other generic libraries configured for a specific binding. + * + * This function is meant to be called by middleware libraries. Will return all configured paths to + * generic libraries. You likely wil want to use the utility functions provided in <CommonAPI/utils.h> + * to do the loading. To arrange and time the loading is responsibility of the middleware only. + * + * @return A vector containing all generic libraries associated with the given middlewareIdentifier. + */ + const std::vector<std::string>& getGenericLibraryPaths(const std::string& middlewareIdentifier) const; + + /** + * Returns the configured default middleware identifier. + * + * If no default has been configured, the empty string "" will be returned. + * + * @return The configured default middleware identifier. + */ + const std::string& getDefaultMiddlewareIdentifier() const; + + private: + Configuration() = default; + + void readConfigFile(std::ifstream& addressConfigFile); + void retrieveCommonApiConfiguration(); + void readEnvironmentVariables(); +}; + + + +} // namespace CommonAPI + +#endif /* COMMONAPI_CONFIGURATION_H_ */ diff --git a/src/CommonAPI/Factory.h b/src/CommonAPI/Factory.h index ede8536..91bafda 100644 --- a/src/CommonAPI/Factory.h +++ b/src/CommonAPI/Factory.h @@ -23,6 +23,7 @@ #include "Proxy.h" #include "Stub.h" #include "types.h" +#include "utils.h" namespace CommonAPI { diff --git a/src/CommonAPI/Factory.hpp b/src/CommonAPI/Factory.hpp index 2f481f5..6ba73f6 100644 --- a/src/CommonAPI/Factory.hpp +++ b/src/CommonAPI/Factory.hpp @@ -19,7 +19,10 @@ Factory::buildProxy(const std::string& participantId, const std::string& domain) { std::shared_ptr<Proxy> abstractMiddlewareProxy = createProxy(_ProxyClass<_AttributeExtensions...>::getInterfaceId(), participantId, serviceName, domain); - return std::make_shared<_ProxyClass<_AttributeExtensions...>>(abstractMiddlewareProxy); + if (abstractMiddlewareProxy) { + return std::make_shared<_ProxyClass<_AttributeExtensions...>>(abstractMiddlewareProxy); + } + return NULL; } template<template<typename ...> class _ProxyClass, typename ... _AttributeExtensions > @@ -43,7 +46,10 @@ Factory::buildProxyWithDefaultAttributeExtension(const std::string& participantI const std::string& domain) { std::shared_ptr<Proxy> abstractMiddlewareProxy = createProxy(DefaultAttributeProxyFactoryHelper<_ProxyClass, _AttributeExtension>::class_t::getInterfaceId(), participantId, serviceName, domain); - return std::make_shared<typename DefaultAttributeProxyFactoryHelper<_ProxyClass, _AttributeExtension>::class_t>(abstractMiddlewareProxy); + if (abstractMiddlewareProxy) { + return std::make_shared<typename DefaultAttributeProxyFactoryHelper<_ProxyClass, _AttributeExtension>::class_t>(abstractMiddlewareProxy); + } + return NULL; } template <template<typename ...> class _ProxyClass, template<typename> class _AttributeExtension> diff --git a/src/CommonAPI/MiddlewareInfo.h b/src/CommonAPI/MiddlewareInfo.h index 6b69691..ffbc21c 100644 --- a/src/CommonAPI/MiddlewareInfo.h +++ b/src/CommonAPI/MiddlewareInfo.h @@ -23,31 +23,16 @@ namespace CommonAPI { class Runtime; -inline int FNV1aHash(const char* s) { - const int FNV_offset_basis = 2166136261u; - const int FNV_prime = 16777619; - - int hashValue = FNV_offset_basis; - for (unsigned int i = 0; i < strlen(s); i++) { - hashValue = (hashValue ^ s[i]) * FNV_prime; - } - return hashValue; -} - - typedef std::shared_ptr<Runtime> (*MiddlewareRuntimeLoadFunction) (); struct MiddlewareInfo { const char* middlewareName_; - const int middlewareId_; MiddlewareRuntimeLoadFunction getInstance_; MiddlewareInfo(const char* middlewareName, MiddlewareRuntimeLoadFunction middlewareRuntimeLoadFunction): middlewareName_(middlewareName), - middlewareId_(FNV1aHash(middlewareName)), - getInstance_(middlewareRuntimeLoadFunction) { -} + getInstance_(middlewareRuntimeLoadFunction) {} }; diff --git a/src/CommonAPI/Runtime.cpp b/src/CommonAPI/Runtime.cpp index f5a8096..83ef63d 100644 --- a/src/CommonAPI/Runtime.cpp +++ b/src/CommonAPI/Runtime.cpp @@ -4,52 +4,350 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "Runtime.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <dlfcn.h> +#include <algorithm> #include <iostream> +#include <unordered_map> +#include <stdexcept> + +#include "Runtime.h" +#include "Configuration.h" +#include "utils.h" namespace CommonAPI { -std::unordered_map<std::string, MiddlewareRuntimeLoadFunction>* registeredRuntimeLoadFunctions_; +static std::unordered_map<std::string, MiddlewareRuntimeLoadFunction>* registeredRuntimeLoadFunctions_; +static bool isDynamic_ = false; +static const char COMMONAPI_LIB_PREFIX[] = "libCommonAPI-"; +static const char MIDDLEWARE_INFO_SYMBOL_NAME[] = "middlewareInfo"; -void Runtime::registerRuntimeLoader(std::string middlewareName, MiddlewareRuntimeLoadFunction middlewareRuntimeLoadFunction) { - if(!registeredRuntimeLoadFunctions_) { + +inline bool Runtime::tryLoadLibrary(const std::string& libraryPath, void** sharedLibraryHandle, MiddlewareInfo** foundMiddlewareInfo) { + //In case we find an already loaded library again while looking for another one, + //there is no need to look at it + if (dlopen(libraryPath.c_str(), RTLD_NOLOAD)) { + return false; + } + //In order to place symbols of the newly loaded library ahead of already resolved symbols, we need + //RTLD_DEEPBIND. This is necessary for this case: A library already is linked at compile time, but while + //trying to resolve another library dynamically we might find the very same library again. + //dlopen() doesn't know about the compile time linked library and will close it if dlclose() ever is + //called, thereby causing memory corruptions and the like. Therefore, we must be able to access the + //middlewareInfo of the newly dlopened library in order to determine whether it already has been linked. + *sharedLibraryHandle = dlopen(libraryPath.c_str(), RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND); + if (sharedLibraryHandle == NULL) { + return false; + } + + *foundMiddlewareInfo = static_cast<MiddlewareInfo*>(dlsym(*sharedLibraryHandle, MIDDLEWARE_INFO_SYMBOL_NAME)); + + //In this context, a resolved value of NULL it is just as invalid as if dlerror() was set. + if (!*foundMiddlewareInfo) { + dlclose(*sharedLibraryHandle); + return false; + } + + if (!(*foundMiddlewareInfo)->middlewareName_ || !(*foundMiddlewareInfo)->getInstance_) { + dlclose(sharedLibraryHandle); + return false; + } + + return true; +} + + +bool Runtime::checkAndLoadLibrary(const std::string& libraryPath, const std::string& requestedBindingIdentifier, bool keepLibrary) { + void* sharedLibraryHandle = NULL; + MiddlewareInfo* foundMiddlewareInfo; + if (!tryLoadLibrary(libraryPath, &sharedLibraryHandle, &foundMiddlewareInfo)) { + return false; + } + + if (foundMiddlewareInfo->middlewareName_ != requestedBindingIdentifier) { + //If library was linked at compile time (and therefore an appropriate runtime loader is registered), + //the library must not be closed! + auto foundRegisteredRuntimeLoader = registeredRuntimeLoadFunctions_->find(foundMiddlewareInfo->middlewareName_); + if (foundRegisteredRuntimeLoader == registeredRuntimeLoadFunctions_->end()) { + dlclose(sharedLibraryHandle); + } + return false; + } + + if (!keepLibrary) { + dlclose(sharedLibraryHandle); + } else { + //Extend visibility to make symbols available to all other libraries loaded afterwards, + //e.g. libraries containing generated binding specific code. + sharedLibraryHandle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_GLOBAL); + if (!sharedLibraryHandle) { + return false; + } + registeredRuntimeLoadFunctions_->insert( {foundMiddlewareInfo->middlewareName_, foundMiddlewareInfo->getInstance_} ); + } + + return true; +} + + +bool Runtime::checkAndLoadDefaultLibrary(std::string& foundBindingIdentifier, const std::string& libraryPath) { + void* sharedLibraryHandle = NULL; + MiddlewareInfo* foundMiddlewareInfo; + if (!tryLoadLibrary(libraryPath, &sharedLibraryHandle, &foundMiddlewareInfo)) { + return false; + } + + //Extend visibility to make symbols available to all other linked libraries, + //e.g. libraries containing generated binding specific code + sharedLibraryHandle = dlopen(libraryPath.c_str(), RTLD_NOW | RTLD_GLOBAL); + if (!sharedLibraryHandle) { + return false; + } + registeredRuntimeLoadFunctions_->insert( {foundMiddlewareInfo->middlewareName_, foundMiddlewareInfo->getInstance_} ); + foundBindingIdentifier = foundMiddlewareInfo->middlewareName_; + + return true; +} + + +const std::vector<std::string> Runtime::readDirectory(const std::string& path) { + std::vector<std::string> result; + struct stat filestat; + + DIR *directory = opendir(path.c_str()); + + if (!directory) { + return std::vector<std::string>(); + } + + struct dirent* entry; + + while ((entry = readdir(directory))) { + const std::string fqnOfEntry = path + entry->d_name; + + if (stat(fqnOfEntry.c_str(), &filestat)) { + continue; + } + if (S_ISDIR(filestat.st_mode)) { + continue; + } + + if (strncmp(COMMONAPI_LIB_PREFIX, entry->d_name, strlen(COMMONAPI_LIB_PREFIX)) != 0) { + continue; + } + + const char* fileNamePtr = entry->d_name; + while ((fileNamePtr = strchr(fileNamePtr + 1, '.'))) { + if (strncmp(".so", fileNamePtr, 3) == 0) { + break; + } + } + + if (fileNamePtr) { + result.push_back(fqnOfEntry); + } + } + + closedir (directory); + + std::sort( result.begin(), result.end() ); + + return result; +} + + +struct LibraryVersion { + int32_t major; + int32_t minor; + int32_t revision; +}; + +bool operator<(LibraryVersion const& lhs, LibraryVersion const& rhs) { + if (lhs.major == rhs.major) { + if (lhs.minor == rhs.minor) { + return lhs.revision < rhs.revision; + } + return lhs.minor < rhs.minor; + } + return lhs.major < rhs.major; +} + + +std::shared_ptr<Runtime> Runtime::checkDynamicLibraries(const std::string& requestedMiddlewareName, LoadState& loadState) { + const std::string& middlewareLibraryPath = Configuration::getInstance().getMiddlewareLibraryPath(requestedMiddlewareName); + + if (middlewareLibraryPath != "") { + if (!checkAndLoadLibrary(middlewareLibraryPath, requestedMiddlewareName, true)) { + //A path for requestedMiddlewareName was configured, but no corresponding library was found + loadState = LoadState::CONFIGURATION_ERROR; + return std::shared_ptr<Runtime>(NULL); + } else { + const std::string currentBinaryFQN = getCurrentBinaryFileFQN(); + const uint32_t lastPathSeparatorPosition = currentBinaryFQN.find_last_of("/\\"); + const std::string currentBinaryPath = currentBinaryFQN.substr(0, lastPathSeparatorPosition + 1); + auto foundRuntimeLoader = registeredRuntimeLoadFunctions_->find(requestedMiddlewareName); + if (foundRuntimeLoader != registeredRuntimeLoadFunctions_->end()) { + return (foundRuntimeLoader->second)(); + } + //One should not get here + loadState = LoadState::BINDING_ERROR; + return std::shared_ptr<Runtime>(NULL); + } + } + + const std::vector<std::string>& librarySearchPaths = Configuration::getInstance().getLibrarySearchPaths(); + + LibraryVersion highestVersionFound = {0, 0, 0}; + std::string fqnOfHighestVersion = ""; + struct stat filestat; + + for (const std::string& singleSearchPath: librarySearchPaths) { + std::vector<std::string> orderedLibraries = readDirectory(singleSearchPath); + + for (const std::string& fqnOfEntry : orderedLibraries) { + std::string versionString; + LibraryVersion currentLibraryVersion = {-1, -1, -1}; + + + const char* fileNamePtr = fqnOfEntry.c_str(); + while ((fileNamePtr = strchr(fileNamePtr + 1, '.'))) { + if (strncmp(".so", fileNamePtr, 3) == 0) { + break; + } + } + + const char* positionOfFirstDot = strchr(fileNamePtr + 1, '.'); + if (positionOfFirstDot) { + versionString = positionOfFirstDot + 1; + } + + std::vector<std::string> versionElements = split(versionString, '.'); + if (versionElements.size() >= 1 && containsOnlyDigits(versionElements[0])) { + currentLibraryVersion.major = strtol(versionElements[0].c_str(), NULL, 0); + } + if (versionElements.size() >= 3 && containsOnlyDigits(versionElements[2])) { + currentLibraryVersion.minor = strtol(versionElements[1].c_str(), NULL, 0); + currentLibraryVersion.revision = strtol(versionElements[2].c_str(), NULL, 0); + } + + if (highestVersionFound < currentLibraryVersion) { + if (!checkAndLoadLibrary(fqnOfEntry, requestedMiddlewareName, false)) { + continue; + } + highestVersionFound = currentLibraryVersion; + fqnOfHighestVersion = fqnOfEntry; + } + } + } + + if (fqnOfHighestVersion != "") { + checkAndLoadLibrary(fqnOfHighestVersion, requestedMiddlewareName, true); + + auto foundRuntimeLoader = registeredRuntimeLoadFunctions_->find(requestedMiddlewareName); + if (foundRuntimeLoader != registeredRuntimeLoadFunctions_->end()) { + std::shared_ptr<Runtime> loadedRuntime = foundRuntimeLoader->second(); + if (!loadedRuntime) { + loadState = LoadState::BINDING_ERROR; + } + return loadedRuntime; + } + } + + loadState = LoadState::NO_LIBRARY_FOUND; + + return std::shared_ptr<Runtime>(); +} + + +std::shared_ptr<Runtime> Runtime::checkDynamicLibraries(LoadState& loadState) { + const std::string& defaultBindingIdentifier = Configuration::getInstance().getDefaultMiddlewareIdentifier(); + if (defaultBindingIdentifier != "") { + return checkDynamicLibraries(defaultBindingIdentifier, loadState); + } + + const std::vector<std::string>& librarySearchPaths = Configuration::getInstance().getLibrarySearchPaths(); + + for (const std::string& singleSearchPath : librarySearchPaths) { + std::vector<std::string> orderedLibraries = readDirectory(singleSearchPath); + + for (const std::string& fqnOfEntry: orderedLibraries) { + std::string foundBindingName; + if (!checkAndLoadDefaultLibrary(foundBindingName, fqnOfEntry)) { + continue; + } + + auto foundRuntimeLoader = registeredRuntimeLoadFunctions_->find(foundBindingName); + if (foundRuntimeLoader != registeredRuntimeLoadFunctions_->end()) { + return (foundRuntimeLoader->second)(); + } + } + } + + loadState = LoadState::NO_LIBRARY_FOUND; + + return std::shared_ptr<Runtime>(); +} + + +void Runtime::registerRuntimeLoader(const std::string& middlewareName, const MiddlewareRuntimeLoadFunction& middlewareRuntimeLoadFunction) { + if (!registeredRuntimeLoadFunctions_) { registeredRuntimeLoadFunctions_ = new std::unordered_map<std::string, MiddlewareRuntimeLoadFunction>(); } - registeredRuntimeLoadFunctions_->insert({middlewareName, middlewareRuntimeLoadFunction}); + if (!isDynamic_) { + registeredRuntimeLoadFunctions_->insert( {middlewareName, middlewareRuntimeLoadFunction}); + } } std::shared_ptr<Runtime> Runtime::load() { + LoadState dummyState; + return load(dummyState); +} + + +std::shared_ptr<Runtime> Runtime::load(LoadState& loadState) { + isDynamic_ = true; + loadState = LoadState::SUCCESS; if(!registeredRuntimeLoadFunctions_) { - registeredRuntimeLoadFunctions_ = new std::unordered_map<std::string, MiddlewareRuntimeLoadFunction> {}; + registeredRuntimeLoadFunctions_ = new std::unordered_map<std::string, MiddlewareRuntimeLoadFunction>(); } - auto begin = registeredRuntimeLoadFunctions_->begin(); + const auto defaultRuntimeLoader = registeredRuntimeLoadFunctions_->begin(); - if (begin != registeredRuntimeLoadFunctions_->end()) { - return (begin->second)(); + if (defaultRuntimeLoader != registeredRuntimeLoadFunctions_->end()) { + return (defaultRuntimeLoader->second)(); } - return std::shared_ptr<Runtime>(NULL); + return checkDynamicLibraries(loadState); } -std::shared_ptr<Runtime> Runtime::load(const std::string& middlewareName) { - if(!registeredRuntimeLoadFunctions_) { - registeredRuntimeLoadFunctions_ = new std::unordered_map<std::string, MiddlewareRuntimeLoadFunction> {}; +std::shared_ptr<Runtime> Runtime::load(const std::string& middlewareIdOrAlias) { + LoadState dummyState; + return load(middlewareIdOrAlias, dummyState); +} + +std::shared_ptr<Runtime> Runtime::load(const std::string& middlewareIdOrAlias, LoadState& loadState) { + isDynamic_ = true; + loadState = LoadState::SUCCESS; + if (!registeredRuntimeLoadFunctions_) { + registeredRuntimeLoadFunctions_ = new std::unordered_map<std::string, MiddlewareRuntimeLoadFunction>(); } - for (auto it = registeredRuntimeLoadFunctions_->begin(); it != registeredRuntimeLoadFunctions_->end(); ++it) { - if(it->first == middlewareName) { - return (it->second)(); - } + const std::string middlewareName = Configuration::getInstance().getMiddlewareNameForAlias(middlewareIdOrAlias); + + auto foundRuntimeLoader = registeredRuntimeLoadFunctions_->find(middlewareName); + if (foundRuntimeLoader != registeredRuntimeLoadFunctions_->end()) { + return (foundRuntimeLoader->second)(); } - return std::shared_ptr<Runtime>(NULL); + return checkDynamicLibraries(middlewareName, loadState); } diff --git a/src/CommonAPI/Runtime.h b/src/CommonAPI/Runtime.h index c0040a8..0a93ccc 100644 --- a/src/CommonAPI/Runtime.h +++ b/src/CommonAPI/Runtime.h @@ -17,12 +17,11 @@ #include "MainLoopContext.h" #include <memory> -#include <fstream> #include <unordered_map> -#include <dlfcn.h> #include <string> #include <cassert> #include <cstring> +#include <mutex> namespace CommonAPI { @@ -40,41 +39,90 @@ class ServicePublisher; */ class Runtime { public: + enum class LoadState { + SUCCESS, + NO_LIBRARY_FOUND, + CONFIGURATION_ERROR, + BINDING_ERROR + }; + + virtual ~Runtime() {} + /** * \brief Loads the default runtime. * - * Loads the runtime for the default middleware binding. This either is the only binding available, - * or the one defined as default in the configuration. + * Loads the runtime for the default middleware binding. This can be + * * One of the middleware bindings that were linked at compile time + * * The first middleware binding that is encountered when resolving bindings at runtime + * * The middleware binding that was configured as default in the corresponding configuration + * file (throws an error if no such binding exists) * - * @return The runtime object for the default binding + * @return The runtime object for the default binding, or null if any error occurred */ static std::shared_ptr<Runtime> load(); /** + * \brief Loads the default runtime and notifies the caller of any errors. + * + * Loads the runtime for the default middleware binding. This can be + * * One of the middleware bindings that were linked at compile time + * * The first middleware binding that is encountered when resolving bindings at runtime + * * The middleware binding that was configured as default in the corresponding configuration + * file (throws an error if no such binding exists) + * + * @param loadState: An enumeration that will be set appropriately after loading has finished or + * aborted. May be used for debugging purposes. + * + * @return The runtime object for the default binding, or null if any error occurred. In the latter + * case, loadState will be set to an appropriate value. + */ + static std::shared_ptr<Runtime> load(LoadState& loadState); + + /** * \brief Loads specified runtime. * - * Loads the runtime for the specified middleware binding. The given middleware ID can be either + * Loads the runtime for the specified middleware binding. The given well known name can be either * the well known name defined by a binding, or a configured alias for a binding. * - * @return The runtime object for specified binding + * @param middlewareIdOrAlias A well known name or an alias for a binding + * + * @return The runtime object for specified binding, or null if any error occurred. + * + * @throw std::invalid_argument if a path for this middlewareId has been configured, but no appropriate library + * could be found there. */ - static std::shared_ptr<Runtime> load(const std::string& middlewareId); + static std::shared_ptr<Runtime> load(const std::string& middlewareIdOrAlias); + + /** + * \brief Loads specified runtime. + * + * Loads the runtime for the specified middleware binding. The given well known name can be either + * the well known name defined by a binding, or a configured alias for a binding. + * + * @param middlewareIdOrAlias A well known name or an alias for a binding. + * @param loadState: An enumeration that will be set appropriately after loading has finished or + * aborted. May be used for debugging purposes. + * + * @return The runtime object for specified binding, or null if any error occurred. In the latter + * case, loadState will be set to an appropriate value. + */ + static std::shared_ptr<Runtime> load(const std::string& middlewareIdOrAlias, LoadState& loadState); /** * \brief Called by bindings to register their runtime loaders. Do not call from applications. * * Called by bindings to register their runtime loaders. Do not call from applications. */ - static void registerRuntimeLoader(std::string middlewareName, MiddlewareRuntimeLoadFunction middlewareRuntimeLoadFunction); - - virtual ~Runtime() {} + static void registerRuntimeLoader(const std::string& middlewareName, const MiddlewareRuntimeLoadFunction& middlewareRuntimeLoadFunction); /** - * \brief Returns new MainLoopContext + * \brief Returns new MainLoopContext. * * Creates and returns a new MainLoopContext object. This context can be used to take * complete control over the order and time of execution of the abstract middleware - * dispatching mechanism. + * dispatching mechanism. Make sure to register all callback functions before subsequently + * handing it to createFactory(), as during creation of the factory object the callbacks may + * already be called. * * @return A new MainLoopContext object */ @@ -100,9 +148,9 @@ class Runtime { * * @return Factory object for this runtime */ - virtual std::shared_ptr<Factory> createFactory(std::shared_ptr<MainLoopContext> mainLoopContext = std::shared_ptr<MainLoopContext>(NULL), - const std::string factoryName = "", - const bool nullOnInvalidName = false); + std::shared_ptr<Factory> createFactory(std::shared_ptr<MainLoopContext> mainLoopContext = std::shared_ptr<MainLoopContext>(NULL), + const std::string factoryName = "", + const bool nullOnInvalidName = false); /** * \brief Create a factory for the loaded runtime. @@ -119,8 +167,8 @@ class Runtime { * * @return Factory object for this runtime */ - virtual std::shared_ptr<Factory> createFactory(const std::string factoryNamey, - const bool nullOnInvalidName = false); + std::shared_ptr<Factory> createFactory(const std::string factoryNamey, + const bool nullOnInvalidName = false); /** * \brief Returns the ServicePublisher object for this runtime. @@ -136,8 +184,20 @@ class Runtime { protected: virtual std::shared_ptr<Factory> doCreateFactory(std::shared_ptr<MainLoopContext> mainLoopContext, - const std::string factoryName, + const std::string& factoryName, const bool nullOnInvalidName = false) = 0; + + private: + static const std::vector<std::string> readDirectory(const std::string& path); + + static std::shared_ptr<Runtime> checkDynamicLibraries(LoadState& loadState); + static std::shared_ptr<Runtime> checkDynamicLibraries(const std::string& middlewareName, LoadState& loadState); + + static bool tryLoadLibrary(const std::string& libraryPath, void** sharedLibraryHandle, MiddlewareInfo** foundMiddlewareInfo); + static bool checkAndLoadLibrary(const std::string& libraryPath, const std::string& requestedMiddlewareName, bool keepLibrary); + static bool checkAndLoadDefaultLibrary(std::string& foundBindingName, const std::string& libraryPath); + + static void closeHandle(void* libraryHandle); }; diff --git a/src/CommonAPI/utils.h b/src/CommonAPI/utils.h new file mode 100644 index 0000000..c1c1c54 --- /dev/null +++ b/src/CommonAPI/utils.h @@ -0,0 +1,340 @@ +/* Copyright (C) 2013 BMW Group + * Author: Manfred Bathelt (manfred.bathelt@bmw.de) + * Author: Juergen Gehring (juergen.gehring@bmw.de) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef COMMONAPI_UTILS_H_ +#define COMMONAPI_UTILS_H_ + +#include <unistd.h> +#include <dirent.h> +#include <dlfcn.h> +#include <sys/stat.h> + +#include <cstring> +#include <string> +#include <sstream> +#include <vector> +#include <algorithm> +#include <iostream> + + +namespace CommonAPI { + + +#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) +# define COMMONAPI_DEPRECATED __attribute__ ((__deprecated__)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# define COMMONAPI_DEPRECATED __declspec(deprecated) +#else +# define COMMONAPI_DEPRECATED +#endif + + +/** + * \brief Returns the fully qualified name of the binary. + * + * @return The name of the currently executing binary. + */ +inline std::string getCurrentBinaryFileFQN() { + char fqnOfBinary[FILENAME_MAX]; + char pathToProcessImage[FILENAME_MAX]; + + sprintf(pathToProcessImage, "/proc/%d/exe", getpid()); + const ssize_t lengthOfFqn = readlink(pathToProcessImage, fqnOfBinary, sizeof(fqnOfBinary) - 1); + + if (lengthOfFqn != -1) { + fqnOfBinary[lengthOfFqn] = '\0'; + return std::string(std::move(fqnOfBinary)); + } else { + return std::string(""); + } +} + +/** + * \brief Splits a std::string according to the given delim-char. + * + * The string will be splitted at each position the delim char is encountered. The delim itself + * will be removed from the result. + * + * @param s: The string that is to be splitted + * @param delim: The character that separates the resulting string tokens in the original string + * @param elems: Reference to the vector that shall be filled with the splitted string elements. + * + * @return A reference to the vector you passed in (elems) + */ +inline std::vector<std::string>& split(const std::string& s, char delim, std::vector<std::string>& elems) { + std::istringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + +/** + * \brief Splits a std::string according to the given delim-char. + * + * The string will be splitted at each position the delim char is encountered. The delim itself + * will be removed from the result. + * + * @param s: The string that is to be splitted + * @param delim: The character that separates the resulting string tokens in the original string + * + * @return A vector containing the splitted string elements. + */ +inline std::vector<std::string> split(const std::string& s, char delim) { + std::vector<std::string> elems; + return split(s, delim, elems); +} + +/** + * \brief Trims whitespaces from beginning and end of a std::string. + * + * @param toTrim: The string that is to be trimmed. + */ +inline void trim(std::string& toTrim) { + toTrim.erase( + toTrim.begin(), + std::find_if(toTrim.begin(), + toTrim.end(), + std::not1(std::ptr_fun<int, int>(std::isspace))) + ); + toTrim.erase( + std::find_if(toTrim.rbegin(), + toTrim.rend(), + std::not1(std::ptr_fun<int, int>(std::isspace))).base(), + toTrim.end() + ); +} + +/** + * \brief Checks whether the given string contains nothing but digits. + * + * @param toCheck: The string that is to be checked on the presence of anything but digits. + * + * @return true if toCheck contains nothing but digits, false otherwise. + */ +inline bool containsOnlyDigits(const std::string& toCheck) { + auto firstNonDigitIt = std::find_if( + toCheck.begin(), + toCheck.end(), + [](char c) { + return !std::isdigit(c); + }); + + return firstNonDigitIt == toCheck.end(); +} + +/** + * \brief Checks whether the given string contains nothing but alphanumeric characters. + * + * @param toCheck: The string that is to be checked on the presence of anything but alphanumeric characters. + * + * @return true if toCheck contains nothing but alphanumeric characters, false otherwise. + */ +inline bool containsOnlyAlphanumericCharacters(const std::string& toCheck) { + auto firstNonAlphanumericCharacterIt = std::find_if( + toCheck.begin(), + toCheck.end(), + [](char c) { + return !std::isalnum(c); + }); + + return firstNonAlphanumericCharacterIt == toCheck.end(); +} + +/** + * \brief Checks whether the given std::string is a valid CommonAPI domain name. + * + * @param domainName: The std::string that is to be checked. + * + * @return true if domainName is a valid CommonAPI domainName, false otherwise. + */ +inline bool isValidDomainName(const std::string& domainName) { + return containsOnlyAlphanumericCharacters(domainName); +} + +/** + * \brief Checks whether the given std::string is a valid CommonAPI service name. + * + * @param serviceName: The std::string that is to be checked. + * + * @return true if serviceName is a valid CommonAPI serviceName, false otherwise. + */ +inline bool isValidServiceName(const std::string& serviceName) { + bool isValid = serviceName[0] != '.' && serviceName[serviceName.size() - 1] != '.'; + + if (isValid) { + std::vector<std::string> splittedServiceName = split(serviceName, '.'); + + for (auto serviceNameElementIt = splittedServiceName.begin(); + serviceNameElementIt != splittedServiceName.end() && isValid; + ++serviceNameElementIt) { + isValid &= containsOnlyAlphanumericCharacters(*serviceNameElementIt); + } + } + + return isValid; +} + +/** + * \brief Checks whether the given std::string is a valid CommonAPI instance ID. + * + * @param instanceId: The std::string that is to be checked. + * + * @return true if instanceId is a valid CommonAPI instance ID, false otherwise. + */ +inline bool isValidInstanceId(const std::string& instanceId) { + //Validation rules for ServiceName and InstanceID are equivalent + return isValidServiceName(instanceId); +} + +/** + * \brief Checks whether the given std::string is a valid CommonAPI address. + * + * @param commonApiAddressName: The std::string that is to be checked. + * + * @return true if commonApiAddress is a valid CommonAPI address, false otherwise. + */ +inline bool isValidCommonApiAddress(const std::string& commonApiAddress) { + std::vector<std::string> splittedAddress = split(commonApiAddress, ':'); + if (splittedAddress.size() != 3) { + return false; + } + return isValidDomainName(splittedAddress[0]) && isValidServiceName(splittedAddress[1]) && isValidInstanceId(splittedAddress[2]); +} + + +/** + * \brief Loads a specific generic library at runtime. + * + * The library will be loaded using dlopen(3) with the flags (RTLD_NOW | RTLD_GLOBAL), if all pre-checks are + * successful. Pre-checks include the verification that the given parameters actually point to a library and + * optionally if the library matches the standard name pattern for CommonAPI generic libraries. The standard + * name pattern is "lib<wellKnownMiddlewareName>Gen-<arbitraryName>.so[.major[.minor.revision]]". + * + * @param wellKnownMiddlewareName: The name of the middleware that requests the loading of the library. + * @param libraryName: The name of the library that shall be loaded. + * @param path: The path at which the library is to be found. path + library name together make up the fully + * qualified name of the library. + * @param checkStandardNamePattern: If set to true, it will be ensured the library matches the CommonAPI + * standard name pattern for generic libraries. This is meant as a safety measure + * to prevent the loading of unnecessary or the wrong libraries. Set to false if + * you are sure about what you are doing. + * @return true if the library could be loaded successfully, false otherwise. + * + * @note The well known middleware name is included as a parameter because the additional libraries normally are needed + * only by specific middlewares. This name however will only be taken into consideration during name checking + * if the checkStandardNamePattern flag is set to true. + */ +inline bool loadGenericLibrary(const std::string& wellKnownMiddlewareName, const std::string& libraryName, const std::string& path, bool checkStandardNamePattern = true) { + std::string fqnOfLibrary = path + libraryName; + struct stat filestat; + if (stat(fqnOfLibrary.c_str(), &filestat)) { + return false; + } + if (S_ISDIR(filestat.st_mode)) { + return false; + } + + if (checkStandardNamePattern) { + const std::string generatedLibPrefix = "lib" + wellKnownMiddlewareName + "Gen-"; + if (strncmp(generatedLibPrefix.c_str(), libraryName.c_str(), generatedLibPrefix.length()) != 0) { + return false; + } + + const char* fileNamePtr = libraryName.c_str(); + while ((fileNamePtr = strchr(fileNamePtr + 1, '.'))) { + if (strncmp(".so", fileNamePtr, 3) == 0) { + break; + } + } + + if (!fileNamePtr) { + return false; + } + } + + dlopen(fqnOfLibrary.c_str(), RTLD_NOW | RTLD_GLOBAL); + return true; +} + +/** + * \brief Loads a specific generic library at runtime. + * + * The library will be loaded using dlopen(3) with the flags (RTLD_NOW | RTLD_GLOBAL), if all pre-checks are + * successful. Pre-checks include the verification that the given parameters actually point to a library and + * optionally if the library matches the standard name pattern for CommonAPI generic libraries. The standard + * name pattern is "lib<wellKnownMiddlewareName>Gen-<arbitraryName>.so[.major[.minor.revision]]". + * + * @param wellKnownMiddlewareName: The name of the middleware that requests the loading of the library. + * @param fqnOfLibrary: The fully qualified name of the library. + * @param checkStandardNamePattern: If set to true, it will be ensured the library matches the CommonAPI + * standard name pattern for generic libraries. This is meant as a safety measure + * to prevent the loading of unnecessary or the wrong libraries. Set to false if + * you are sure about what you are doing. + * @return true if the library could be loaded successfully, false otherwise. + * + * @note The well known middleware name is included as a parameter because the additional libraries normally are needed + * only by specific middlewares. This name however will only be taken into consideration during name checking + * if the checkStandardNamePattern flag is set to true. + */ +inline bool loadGenericLibrary(const std::string& wellKnownMiddlewareName, + const std::string& fqnOfLibrary, + bool checkStandardNamePattern = true) { + uint32_t position = fqnOfLibrary.find_last_of("/\\"); + std::string path = fqnOfLibrary.substr(0, position + 1); + std::string file = fqnOfLibrary.substr(position + 1); + return loadGenericLibrary(wellKnownMiddlewareName, file, path, checkStandardNamePattern); +} + + +/** + * \brief Searches the given path for additional generic CommonAPI libraries and loads them. + * + * All libraries for which the pre-checks are successful will be loaded using dlopen(3) with the flags + * (RTLD_NOW | RTLD_GLOBAL). Pre-checks include the verification that the given parameters actually point + * to a library and if the library matches the standard name pattern for CommonAPI generic libraries. + * The standard name pattern is "lib<wellKnownMiddlewareName>Gen-<arbitraryName>.so[.major[.minor.revision]]". + * + * @param wellKnownMiddlewareName: The name of the middleware that requests the loading of the library. Will + * be used to perform the name check. + * @param singleSearchPath: The directory that is to be searched for libraries. + */ +inline void findAndLoadGenericLibraries(const std::string& requestedMiddlewareName, const std::string& singleSearchPath) { + DIR *directory = opendir(singleSearchPath.c_str()); + + if (directory != NULL) { + struct dirent* entry; + + while ((entry = readdir(directory))) { + loadGenericLibrary(requestedMiddlewareName, entry->d_name, singleSearchPath, true); + } + } +} + +/** + * \brief Searches the given paths for additional generic CommonAPI libraries and loads them. + * + * All libraries for which the pre-checks are successful will be loaded using dlopen(3) with the flags + * (RTLD_NOW | RTLD_GLOBAL). Pre-checks include the verification that the given parameters actually point + * to a library and if the library matches the standard name pattern for CommonAPI generic libraries. + * The standard name pattern is "lib<wellKnownMiddlewareName>Gen-<arbitraryName>.so[.major[.minor.revision]]". + * + * @param wellKnownMiddlewareName: The name of the middleware that requests the loading of the library. Will + * be used to perform the name check. + * @param searchPaths: The directories that are to be searched for libraries. + */ +inline void findAndLoadGenericLibraries(const std::string& requestedMiddlewareName, const std::vector<std::string>& searchPaths) { + for (const std::string& singleSearchPath : searchPaths) { + findAndLoadGenericLibraries(requestedMiddlewareName, singleSearchPath.c_str()); + } +} + + +} //namespace CommonAPI + + +#endif /* COMMONAPI_UTILS_H_ */ |