summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksandar Kanchev <kanchev@itestra.com>2013-07-24 09:50:18 +0200
committerGerrit Code Review <qqmthk1@lpmodthk02.bmwgroup.net>2013-07-24 09:50:18 +0200
commit4accbb4a2c1bcefe15c7aa3e698c0ff79ed8fdac (patch)
tree9ddacb6e4068c56680186ca22b4396d8826ee803
parentbe1761cff29995a03337319effcadb07b34cde30 (diff)
parent751e239d58f7382799561190369aa7b480dc6db3 (diff)
downloadgenivi-common-api-runtime-4accbb4a2c1bcefe15c7aa3e698c0ff79ed8fdac.tar.gz
Merge "Introduced dynamic loading of middleware bindings and other generic libraries."
-rw-r--r--Makefile.am6
-rw-r--r--README105
-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
11 files changed, 1142 insertions, 57 deletions
diff --git a/Makefile.am b/Makefile.am
index 8a94c7c..128f100 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -34,7 +34,8 @@ maintainer-clean-local:
lib_LTLIBRARIES = libCommonAPI.la
libCommonAPI_la_SOURCES = \
- src/CommonAPI/Runtime.cpp
+ src/CommonAPI/Runtime.cpp \
+ src/CommonAPI/Configuration.cpp
CommonAPI_includedir=$(includedir)/CommonAPI-${VERSION}/CommonAPI
CommonAPI_include_HEADERS = \
@@ -57,7 +58,8 @@ CommonAPI_include_HEADERS = \
src/CommonAPI/ServicePublisher.h \
src/CommonAPI/ServicePublisher.hpp \
src/CommonAPI/Stub.h \
- src/CommonAPI/types.h
+ src/CommonAPI/types.h \
+ src/CommonAPI/utils.h
libCommonAPI_la_LIBADD = -ldl
libCommonAPI_la_LDFLAGS = \
diff --git a/README b/README
index 0813efb..019a3f5 100644
--- a/README
+++ b/README
@@ -60,6 +60,111 @@ the other way of acchieving the same (the way pointed to by the marked elements)
Elements marked as deprecated will be removed once we have reliable and comprehensive feedback telling us they
are not used anymore by anybody, but they will remain in Common API for compatibility reasons at least until then.
+
+== Fully Dynamic loading of middleware bindings
+
+CommonAPI supports the loading of middleware specific libraries at runtime, without even linking them to the executable beforehand.
+For this purpose, each middleware binding library provides a short well known name that can be used to identify the library.
+If such a well known name is passed as an argument to _CommonAPI::Runtime::load()_, CommonAPI will try to dynamically resolve the
+given name (i.e. find an appropriate library for it), if it is not already provided by a library that was linked at compile time.
+In order to resolve a middleware specific library, the default search paths _/usr/lib_ and _/usr/local/lib/_ will be searched for
+all libraries that match the name pattern "libCommonAPI-<arbitraryName>.so[.major[.minor.revision]]". This naming constraint
+exists in order to prevent too many libraries to be opened and closed again, as each of the libraries found will be searched for the
+presence of the identifying well known name.
+If there are more libraries for the same binding, the library with the higher version will take precedence.
+The resolution procedure can be extended by providing an environment variable with additonal search paths (see below),
+or by specifying a specific path for a specific binding using configuration files (see below). +
+NOTE: If you call _CommonAPI::Runtime::load()_ without an parameter when no middleware library has been linked at compile time,
+ CommonAPI will load the first middleware library it encounters on the default and specified search paths, regardless of
+ its version.
+
+
+== Configuring CommonAPI
+
+It is possible to provide additional configuration parameters to tailor CommonAPI to your specific system architecture.
+This can be done either by providing configuration files or by setting appropriate environment variables.
+
+
+=== Configuration Files
+
+Each CommonAPI configuration file will define additional parameters for specific categories.
+Which categories and which parameters for each of those categories are available will be detailed below.
+
+All parameters for all categories are optional. For each omitted parameter a reasonable default will be set. Because of
+this, it is not mandatory to provide a config file unless you want to alter any of the configurable default values.
+
+CommonAPI config files can be defined locally per binary, globally per binary or globally for all binaries.
+If more than one config file is defined for a given binary (e.g. one locally and one globally) and a given category
+is defined in several of these config files, for each parameter that may be provided for this category the value found
+in the most specific config file will take precedence. If a category is defined several times within the same config file,
+the first occurrence of each parameter will take precedence.
+
+All categories and all parameters are separated from each other by one or more newline characters.
+
+CommonAPI Config files have to be named this way:
+----
+# Binary local: "<FqnOfBinary>.conf", e.g. "/usr/bin/myBinary.conf" if the binary is "/usr/bin/myBinary"
+# Binary global: "/etc/CommonApi/<NameOfBinary>.conf", e.g. "/etc/CommonAPI/myBinary.conf"
+# Global: "/etc/CommonAPI/CommonAPI.conf"
+----
+
+==== Available categories
+
+.Well known names of specific middleware bindings
+Allows to set parameters that influence the loading procedure of specific middleware bindings.
+The syntax is:
+
+==========================
+{binding:<well known binding name>}
+libpath=<Fully qualified name of the library of the binding>
+alias=<One or more desired aliases for the binding, separated by ":">
+genpath=<One or more fully qualified names to libraries containing additional (generated) code for this binding, separated by ":">
+default
+==========================
+
+* *libpath*: Provides a fully qualified name that replaces the search path when trying to dynamically load the identified binding.
+ The library found at libpath will take precedence over all other dynamically discoverable libraries for this binding. +
+ NOTE: If a library for the specified middleware binding is linked to the binary already, this parameter will have no effect. +
+ WARNING: _Not_ finding an appropriate library at libpath is considered to be an error! In this case, no further attempts to resolve
+ the library will be made. If you want to have an explicit error state, call the overloaded _Runtime::load()_
+ functions and pass in an instance of _Runtime::LoadState_ as argument.
+* *alias*: In order to load a specific middleware binding, one normally has to know the well known name of the middleware
+ (e.g. "DBus" for the D-Bus middleware binding) and pass this name as parameter when calling _CommonAPI::Runtime::load("<name>")_.
+ _alias_ maps the well known name for this purpose to one or more arbitrary aliases, thereby decoupling the loading of a specific
+ middleware binding from its specific name. +
+ NOTE: You MAY specify this parameter more than once for a binding. The effect will be the same as if you had one alias parameter
+ specifying the exact same names separated by ":". +
+ NOTE: If the same alias is specified more than once, only the first occurrence of the alias will be considered. +
+ WARNING: As CommonAPI itself does not know about which well known middleware names there are, it is possible to specify the well known
+ name of an actual binding as an alias for any other middleware binding. In this case, the actual middleware binding will not
+ be accessible any longer, unless you specify another unique alias for it.
+* *genpath*: Specifies one or more paths at which a generic library containing additional (e.g. generated middleware and interface specific)
+ code for the middleware binding is to be found. This additional code will be injected when the specific middleware considers
+ it to be the right time to do so. +
+ NOTE: You MAY specify this parameter more than once for a binding. The effect will be the same as if you had one genpath parameter
+ specifying the exact same values separated by ":". +
+ NOTE: If _No_ such parameter is defined, the standard search paths "/usr/lib" and "/usr/local/lib" plus any additional paths defined in
+ the environment variable COMMONAPI_BINDING_PATH (see below) will be searched for any libraries that match the name pattern
+ "lib<wellKnownMiddlewareName>Gen-<arbitraryName>.so[.major[.minor.revision]]". All matching libraries will be loaded. +
+ WARNING: _Not_ finding an appropriate library at any single one of the defined genpaths may result in undefined behavior. +
+ NOTE FOR DEVELOPERS: The _genpath_ parameter will be parsed by the CommonAPI framework and stored in _CommonAPI::Configuration_. Actually loading the
+ libraries and following the rules described here however is task of the specific middleware binding. You might want to use
+ the convenience methods provided in <CommonAPI/utils.h> for this purpose. By taking control of the actual proceedings,
+ you may introduce additional mechanisms of discovering and loading such libraries, and you can defer the loading of the
+ libraries until you deem it to be the right time to do so.
+* *default*: Specifies the library for this binding as the default that is to be loaded if no parameter is given to _CommonAPI::Runtime::load()_. +
+ WARNING: _Not_ finding an appropriate library for a configured default binding at neither specified nor the default paths is considered to be an error!
+ In this case, no further attempts to find another default library will be made! If you want to have an explicit error state, call the
+ overloaded _Runtime::load()_ functions and pass in an instance of _Runtime::LoadState_ as argument.
+
+
+=== Environment Variables
+
+* COMMONAPI_BINDING_PATH: By default, the standard paths "/usr/lib" and "/usr/local/lib" will be searched for binding libraries that are
+ loaded dynamically (i.e. at runtime without linking them to the binary beforehand). All paths defined in this environment variable
+ will take precedence over those two default paths. Separator between several paths is ":".
+
+
== Working on the code & contribution
.First get the code from the git:
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_ */