summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPhilip Rauwolf <rauwolf@itestra.de>2013-07-08 16:55:56 +0200
committerPhilip Rauwolf <rauwolf@itestra.de>2013-07-08 16:55:56 +0200
commit751e239d58f7382799561190369aa7b480dc6db3 (patch)
tree25abd68d9429b1b0a2c3bc3f9c13cdcd7d4112bf /src
parent1891587b43da42d99ffab26517972523a6f80123 (diff)
downloadgenivi-common-api-runtime-751e239d58f7382799561190369aa7b480dc6db3.tar.gz
Introduced dynamic loading of middleware bindings and other generic
libraries. Dynamic loading of middleware libraries including possibilities to configure the loading process now is available. Also added utility functions on CommonAPI level to support loading of libraries of generated code / other generic libraries. Configuration now has its own source module. Added several unit tests to confirm correctness. gitignore, README and inline documentation updated accordingly. Change-Id: Ia11d91a5f8de5b8bbb2ae9844324f050a926579e
Diffstat (limited to 'src')
-rw-r--r--src/CommonAPI/CommonAPI.h1
-rw-r--r--src/CommonAPI/Configuration.cpp166
-rw-r--r--src/CommonAPI/Configuration.h121
-rw-r--r--src/CommonAPI/Factory.h1
-rw-r--r--src/CommonAPI/Factory.hpp10
-rw-r--r--src/CommonAPI/MiddlewareInfo.h17
-rw-r--r--src/CommonAPI/Runtime.cpp334
-rw-r--r--src/CommonAPI/Runtime.h98
-rw-r--r--src/CommonAPI/utils.h340
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_ */