| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef LIBMINIFI_INCLUDE_CORE_CLASSLOADER_H_ |
| #define LIBMINIFI_INCLUDE_CORE_CLASSLOADER_H_ |
| |
| #include <utility> |
| #include <mutex> |
| #include <vector> |
| #include <string> |
| #include <map> |
| #include <memory> |
| #include "utils/StringUtils.h" |
| #ifndef WIN32 |
| #include <dlfcn.h> |
| #define DLL_EXPORT |
| #else |
| #define WIN32_LEAN_AND_MEAN 1 |
| #include <Windows.h> // Windows specific libraries for collecting software metrics. |
| #include <Psapi.h> |
| #pragma comment(lib, "psapi.lib" ) |
| #define DLL_EXPORT __declspec(dllexport) |
| #endif |
| #include "core/Core.h" |
| #include "io/BufferStream.h" |
| |
| namespace org { |
| namespace apache { |
| namespace nifi { |
| namespace minifi { |
| namespace core { |
| |
| #define RESOURCE_FAILURE -1 |
| |
| #define RESOURCE_SUCCESS 1 |
| |
| #ifdef WIN32 |
| #define RTLD_LAZY 0 |
| #define RTLD_NOW 0 |
| |
| #define RTLD_GLOBAL (1 << 1) |
| #define RTLD_LOCAL (1 << 2) |
| #endif |
| |
| /** |
| * Class used to provide a global initialization and deinitialization function for an ObjectFactory. |
| * Calls to instances of all ObjectFactoryInitializers are done under a unique lock. |
| */ |
| class ObjectFactoryInitializer { |
| public: |
| virtual ~ObjectFactoryInitializer() = default; |
| |
| /** |
| * This function is be called before the ObjectFactory is used. |
| * @return whether the initialization was successful. If false, deinitialize will NOT be called. |
| */ |
| virtual bool initialize() = 0; |
| |
| /** |
| * This function will be called after the ObjectFactory is not needed anymore. |
| */ |
| virtual void deinitialize() = 0; |
| }; |
| |
| /** |
| * Factory that is used as an interface for |
| * creating processors from shared objects. |
| */ |
| class ObjectFactory { |
| public: |
| ObjectFactory(const std::string &group) // NOLINT |
| : group_(group) { |
| } |
| |
| ObjectFactory() = default; |
| |
| /** |
| * Virtual destructor. |
| */ |
| virtual ~ObjectFactory() = default; |
| |
| /** |
| * Create a shared pointer to a new processor. |
| */ |
| virtual std::shared_ptr<CoreComponent> create(const std::string &name) { |
| return nullptr; |
| } |
| |
| /** |
| * Create a shared pointer to a new processor. |
| */ |
| virtual CoreComponent *createRaw(const std::string &name) { |
| return nullptr; |
| } |
| |
| /** |
| * Create a shared pointer to a new processor. |
| */ |
| virtual std::shared_ptr<CoreComponent> create(const std::string &name, utils::Identifier & uuid) { |
| return nullptr; |
| } |
| |
| /** |
| * Create a shared pointer to a new processor. |
| */ |
| virtual CoreComponent* createRaw(const std::string &name, utils::Identifier & uuid) { |
| return nullptr; |
| } |
| |
| /** |
| * Returns an initializer for the factory. |
| */ |
| virtual std::unique_ptr<ObjectFactoryInitializer> getInitializer() { |
| return nullptr; |
| } |
| |
| /** |
| * Gets the name of the object. |
| * @return class name of processor |
| */ |
| virtual std::string getName() = 0; |
| |
| virtual std::string getGroupName() const { |
| return group_; |
| } |
| |
| virtual std::string getClassName() = 0; |
| /** |
| * Gets the class name for the object |
| * @return class name for the processor. |
| */ |
| virtual std::vector<std::string> getClassNames() = 0; |
| |
| virtual std::unique_ptr<ObjectFactory> assign(const std::string &class_name) = 0; |
| |
| private: |
| std::string group_; |
| }; |
| /** |
| * Factory that is used as an interface for |
| * creating processors from shared objects. |
| */ |
| template<class T> |
| class DefautObjectFactory : public ObjectFactory { |
| public: |
| DefautObjectFactory() { |
| className = core::getClassName<T>(); |
| } |
| |
| DefautObjectFactory(const std::string &group_name) // NOLINT |
| : ObjectFactory(group_name) { |
| className = core::getClassName<T>(); |
| } |
| /** |
| * Virtual destructor. |
| */ |
| virtual ~DefautObjectFactory() = default; |
| |
| /** |
| * Create a shared pointer to a new processor. |
| */ |
| virtual std::shared_ptr<CoreComponent> create(const std::string &name) { |
| std::shared_ptr<T> ptr = std::make_shared<T>(name); |
| return std::static_pointer_cast<CoreComponent>(ptr); |
| } |
| |
| /** |
| * Create a shared pointer to a new processor. |
| */ |
| virtual std::shared_ptr<CoreComponent> create(const std::string &name, utils::Identifier & uuid) { |
| std::shared_ptr<T> ptr = std::make_shared<T>(name, uuid); |
| return std::static_pointer_cast<CoreComponent>(ptr); |
| } |
| |
| /** |
| * Create a shared pointer to a new processor. |
| */ |
| virtual CoreComponent* createRaw(const std::string &name) { |
| T *ptr = new T(name); |
| return dynamic_cast<CoreComponent*>(ptr); |
| } |
| |
| /** |
| * Create a shared pointer to a new processor. |
| */ |
| virtual CoreComponent* createRaw(const std::string &name, utils::Identifier &uuid) { |
| T *ptr = new T(name, uuid); |
| return dynamic_cast<CoreComponent*>(ptr); |
| } |
| |
| /** |
| * Gets the name of the object. |
| * @return class name of processor |
| */ |
| virtual std::string getName() { |
| return className; |
| } |
| |
| /** |
| * Gets the class name for the object |
| * @return class name for the processor. |
| */ |
| virtual std::string getClassName() { |
| return className; |
| } |
| |
| virtual std::vector<std::string> getClassNames() { |
| std::vector<std::string> container; |
| container.push_back(className); |
| return container; |
| } |
| |
| virtual std::unique_ptr<ObjectFactory> assign(const std::string &class_name) { |
| return nullptr; |
| } |
| |
| protected: |
| std::string className; |
| }; |
| |
| /** |
| * Function that is used to create the |
| * processor factory from the shared object. |
| */ |
| typedef ObjectFactory* createFactory(); |
| |
| /** |
| * Processor class loader that accepts |
| * a variety of mechanisms to load in shared |
| * objects. |
| */ |
| class ClassLoader { |
| public: |
| static ClassLoader &getDefaultClassLoader(); |
| |
| /** |
| * Constructor. |
| */ |
| ClassLoader(); |
| |
| ~ClassLoader() { |
| for (auto& initializer : initializers_) { |
| initializer->deinitialize(); |
| } |
| loaded_factories_.clear(); |
| for (auto ptr : dl_handles_) { |
| dlclose(ptr); |
| } |
| } |
| |
| /** |
| * Sets the class loader |
| * @param name name of class loader |
| * @param loader loader unique ptr |
| * @return result of insertion |
| */ |
| bool setClassLoader(const std::string &name, std::unique_ptr<ClassLoader> loader) { |
| if (nullptr != getClassLoader(name)) { |
| throw std::runtime_error("Cannot have more than one classloader with the same name"); |
| } |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| // return validation that we inserted the class loader |
| return class_loaders_.insert(std::make_pair(name, std::move(loader))).second; |
| } |
| |
| /** |
| * Retrieves a class loader |
| * @param name name of class loader |
| * @return class loader ptr if it exists, nullptr if it does not |
| */ |
| ClassLoader *getClassLoader(const std::string &name) { |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| auto cld = class_loaders_.find(name); |
| if (cld != class_loaders_.end()) { |
| cld->second.get(); |
| } |
| return nullptr; |
| } |
| |
| /** |
| * Register the file system resource. |
| * This will attempt to load objects within this resource. |
| * @return return code: RESOURCE_FAILURE or RESOURCE_SUCCESS |
| */ |
| uint16_t registerResource(const std::string &resource, const std::string &resourceName); |
| |
| /** |
| * Register a class with the give ProcessorFactory |
| */ |
| void registerClass(const std::string &name, std::unique_ptr<ObjectFactory> factory) { |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| if (loaded_factories_.find(name) != loaded_factories_.end()) { |
| return; |
| } |
| |
| auto initializer = factory->getInitializer(); |
| if (initializer != nullptr) { |
| initializer->initialize(); |
| initializers_.emplace_back(std::move(initializer)); |
| } |
| |
| auto canonical_name = factory->getClassName(); |
| |
| auto group_name = factory->getGroupName(); |
| |
| module_mapping_[group_name].push_back(canonical_name); |
| if (canonical_name != name) |
| class_to_group_[canonical_name] = group_name; |
| class_to_group_[name] = group_name; |
| |
| loaded_factories_.insert(std::make_pair(name, std::move(factory))); |
| } |
| |
| std::vector<std::string> getClasses(const std::string &group) { |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| return module_mapping_[group]; |
| } |
| |
| std::vector<std::string> getGroups() { |
| std::vector<std::string> groups; |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| for (auto & resource : loaded_factories_) { |
| groups.push_back(resource.first); |
| } |
| return groups; |
| } |
| |
| std::vector<std::string> getClasses() { |
| std::vector<std::string> groups; |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| for (auto & resource : loaded_factories_) { |
| if (nullptr != resource.second) { |
| auto classes = resource.second->getClassNames(); |
| groups.insert(groups.end(), classes.begin(), classes.end()); |
| } else { |
| } |
| } |
| return groups; |
| } |
| |
| std::string getGroupForClass(const std::string &class_name) { |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| auto factory_entry = class_to_group_.find(class_name); |
| if (factory_entry != class_to_group_.end()) { |
| return factory_entry->second; |
| } else { |
| return ""; |
| } |
| } |
| |
| /** |
| * Instantiate object based on class_name |
| * @param class_name class to create |
| * @param make_shared_ptr creates a shared ptr of the given type name |
| * @return nullptr, object created from class_name definition, or make_shared of T |
| */ |
| template<class T> |
| std::shared_ptr<T> instantiate(const std::string &class_name, bool make_shared_ptr = true); |
| |
| /** |
| * Instantiate object based on class_name |
| * @param class_name class to create |
| * @param uuid uuid of object |
| * @return nullptr or object created from class_name definition. |
| */ |
| template<class T = CoreComponent> |
| std::shared_ptr<T> instantiate(const std::string &class_name, const std::string &name); |
| |
| /** |
| * Instantiate object based on class_name |
| * @param class_name class to create |
| * @param uuid uuid of object |
| * @return nullptr or object created from class_name definition. |
| */ |
| template<class T = CoreComponent> |
| std::shared_ptr<T> instantiate(const std::string &class_name, utils::Identifier & uuid); |
| |
| /** |
| * Instantiate object based on class_name |
| * @param class_name class to create |
| * @param uuid uuid of object |
| * @return nullptr or object created from class_name definition. |
| */ |
| template<class T = CoreComponent> |
| T *instantiateRaw(const std::string &class_name, const std::string &name); |
| |
| /** |
| * Instantiate object based on class_name |
| * @param class_name class to create |
| * @param uuid uuid of object |
| * @return nullptr or object created from class_name definition. |
| */ |
| template<class T = CoreComponent> |
| T *instantiateRaw(const std::string &class_name, utils::Identifier & uuid); |
| |
| protected: |
| #ifdef WIN32 |
| |
| // base_object doesn't have a handle |
| std::map< HMODULE, std::string > resource_mapping_; |
| |
| std::string error_str_; |
| std::string current_error_; |
| |
| void store_error() { |
| auto error = GetLastError(); |
| |
| if (error == 0) { |
| error_str_ = ""; |
| return; |
| } |
| |
| LPSTR messageBuffer = nullptr; |
| size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, |
| NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); |
| |
| current_error_ = std::string(messageBuffer, size); |
| |
| // Free the buffer. |
| LocalFree(messageBuffer); |
| } |
| |
| void *dlsym(void *handle, const char *name) { |
| FARPROC symbol; |
| |
| symbol = GetProcAddress((HMODULE)handle, name); |
| |
| if (symbol == nullptr) { |
| store_error(); |
| |
| for (auto hndl : resource_mapping_) { |
| symbol = GetProcAddress((HMODULE)hndl.first, name); |
| if (symbol != nullptr) { |
| break; |
| } |
| } |
| } |
| |
| #ifdef _MSC_VER |
| #pragma warning(suppress: 4054 ) |
| #endif |
| return reinterpret_cast<void*>(symbol); |
| } |
| |
| const char *dlerror(void) { |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| |
| error_str_ = current_error_; |
| |
| current_error_ = ""; |
| |
| return error_str_.c_str(); |
| } |
| |
| void *dlopen(const char *file, int mode) { |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| HMODULE object; |
| uint32_t uMode = SetErrorMode(SEM_FAILCRITICALERRORS); |
| if (nullptr == file) { |
| HMODULE allModules[1024]; |
| HANDLE current_process_id = GetCurrentProcess(); |
| DWORD cbNeeded; |
| object = GetModuleHandle(NULL); |
| |
| if (!object) |
| store_error(); |
| if (EnumProcessModules(current_process_id, allModules, |
| sizeof(allModules), &cbNeeded) != 0) { |
| for (uint32_t i = 0; i < cbNeeded / sizeof(HMODULE); i++) { |
| // Get the full path to the module's file. |
| resource_mapping_.insert(std::make_pair(allModules[i], "minifi-system")); |
| } |
| } |
| } else { |
| char lpFileName[MAX_PATH]; |
| int i; |
| |
| for (i = 0; i < sizeof(lpFileName) - 1; i++) { |
| if (!file[i]) |
| break; |
| else if (file[i] == '/') |
| lpFileName[i] = '\\'; |
| else |
| lpFileName[i] = file[i]; |
| } |
| lpFileName[i] = '\0'; |
| object = LoadLibraryEx(lpFileName, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH); |
| if (!object) |
| store_error(); |
| else if ((mode & RTLD_GLOBAL)) |
| resource_mapping_.insert(std::make_pair(object, lpFileName)); |
| } |
| |
| /* Return to previous state of the error-mode bit flags. */ |
| SetErrorMode(uMode); |
| |
| return reinterpret_cast<void*>(object); |
| } |
| |
| int dlclose(void *handle) { |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| |
| HMODULE object = (HMODULE)handle; |
| BOOL ret; |
| |
| current_error_ = ""; |
| ret = FreeLibrary(object); |
| |
| resource_mapping_.erase(object); |
| |
| ret = !ret; |
| |
| return static_cast<int>(ret); |
| } |
| |
| #endif |
| |
| std::map<std::string, std::vector<std::string>> module_mapping_; |
| |
| std::map<std::string, std::unique_ptr<ObjectFactory>> loaded_factories_; |
| |
| std::map<std::string, std::string> class_to_group_; |
| |
| // other class loaders |
| |
| std::map<std::string, std::unique_ptr<ClassLoader>> class_loaders_; |
| |
| std::mutex internal_mutex_; |
| |
| std::vector<void *> dl_handles_; |
| |
| std::vector<std::unique_ptr<ObjectFactoryInitializer>> initializers_; |
| }; |
| |
| template<class T> |
| std::shared_ptr<T> ClassLoader::instantiate(const std::string &class_name, bool make_shared_ptr) { |
| const auto ret = instantiate<T>(class_name, class_name); |
| if (nullptr == ret && make_shared_ptr) { |
| return std::make_shared<T>(class_name); |
| } |
| return ret; |
| } |
| |
| template<class T> |
| std::shared_ptr<T> ClassLoader::instantiate(const std::string &class_name, const std::string &name) { |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| auto factory_entry = loaded_factories_.find(class_name); |
| if (factory_entry != loaded_factories_.end()) { |
| auto obj = factory_entry->second->create(name); |
| return std::dynamic_pointer_cast<T>(obj); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| template<class T> |
| std::shared_ptr<T> ClassLoader::instantiate(const std::string &class_name, utils::Identifier &uuid) { |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| auto factory_entry = loaded_factories_.find(class_name); |
| if (factory_entry != loaded_factories_.end()) { |
| auto obj = factory_entry->second->create(class_name, uuid); |
| return std::dynamic_pointer_cast<T>(obj); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| template<class T> |
| T *ClassLoader::instantiateRaw(const std::string &class_name, const std::string &name) { |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| auto factory_entry = loaded_factories_.find(class_name); |
| if (factory_entry != loaded_factories_.end()) { |
| auto obj = factory_entry->second->createRaw(name); |
| return dynamic_cast<T*>(obj); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| template<class T> |
| T *ClassLoader::instantiateRaw(const std::string &class_name, utils::Identifier & uuid) { |
| std::lock_guard<std::mutex> lock(internal_mutex_); |
| auto factory_entry = loaded_factories_.find(class_name); |
| if (factory_entry != loaded_factories_.end()) { |
| auto obj = factory_entry->second->createRaw(class_name, uuid); |
| return dynamic_cast<T*>(obj); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| } // namespace core |
| } // namespace minifi |
| } // namespace nifi |
| } // namespace apache |
| } // namespace org |
| |
| #endif // LIBMINIFI_INCLUDE_CORE_CLASSLOADER_H_ |