This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/cxx in repository https://gitbox.apache.org/repos/asf/celix.git
commit 2d6327e6918723bfd082830b6ddcc5fa2cd60798 Author: Pepijn Noltes <[email protected]> AuthorDate: Sun Jun 16 12:50:27 2019 +0200 CELIX-370: Initial commit for CompnentManager (DM) for C++ Celix --- .../topology_manager/CMakeLists.txt | 2 +- libs/framework_cxx/CMakeLists.txt | 1 + libs/framework_cxx/gtest/CMakeLists.txt | 1 + .../gtest/src/ComponentManager_tests.cc | 61 +++++ .../framework_cxx/include/celix/ComponentManager.h | 288 +++++++++++++++++++++ libs/framework_cxx/include/celix/Framework.h | 2 +- libs/framework_cxx/src/ComponentManager.cc | 102 ++++++++ libs/framework_cxx/src/Framework.cc | 6 +- libs/registry/include/celix/Properties.h | 54 ++-- libs/registry/src/ServiceRegistry.cc | 2 +- 10 files changed, 496 insertions(+), 23 deletions(-) diff --git a/bundles/remote_services/topology_manager/CMakeLists.txt b/bundles/remote_services/topology_manager/CMakeLists.txt index 9ee218f..211a31e 100644 --- a/bundles/remote_services/topology_manager/CMakeLists.txt +++ b/bundles/remote_services/topology_manager/CMakeLists.txt @@ -41,7 +41,7 @@ if (RSA_TOPOLOGY_MANAGER) include_directories(${CPPUTEST_INCLUDE_DIR}) include_directories(${CPPUTEST_EXT_INCLUDE_DIR}) add_subdirectory(tms_tst) - endif (ENABLE_TESTING) + endif () install_celix_bundle(rsa_topology_manager EXPORT celix COMPONENT rsa) #Setup target aliases to match external usage diff --git a/libs/framework_cxx/CMakeLists.txt b/libs/framework_cxx/CMakeLists.txt index c4604ac..f5c3164 100644 --- a/libs/framework_cxx/CMakeLists.txt +++ b/libs/framework_cxx/CMakeLists.txt @@ -27,6 +27,7 @@ add_library(celix_framework_cxx SHARED src/Framework.cc src/BundleContext.cc src/Bundle.cc + src/ComponentManager.cc ) target_include_directories(celix_framework_cxx PRIVATE src) target_include_directories(celix_framework_cxx PUBLIC include) diff --git a/libs/framework_cxx/gtest/CMakeLists.txt b/libs/framework_cxx/gtest/CMakeLists.txt index 4e76ed2..e12c856 100644 --- a/libs/framework_cxx/gtest/CMakeLists.txt +++ b/libs/framework_cxx/gtest/CMakeLists.txt @@ -18,6 +18,7 @@ set(SOURCES src/main.cc src/Framework_tests.cc + src/ComponentManager_tests.cc ) add_executable(celix_framework_cxx_tests ${SOURCES}) target_link_libraries(celix_framework_cxx_tests PRIVATE gtest celix_framework_cxx glog::glog) diff --git a/libs/framework_cxx/gtest/src/ComponentManager_tests.cc b/libs/framework_cxx/gtest/src/ComponentManager_tests.cc new file mode 100644 index 0000000..f9a42ad --- /dev/null +++ b/libs/framework_cxx/gtest/src/ComponentManager_tests.cc @@ -0,0 +1,61 @@ +/** + *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. + */ + +#include <atomic> + +#include "gtest/gtest.h" + +#include "celix/Framework.h" +#include "celix/ComponentManager.h" + +class ComponentManagerTest : public ::testing::Test { +public: + celix::Framework& framework() { return fw; } +private: + celix::Framework fw{}; +}; + + +TEST_F(ComponentManagerTest, CreateDestroy) { + auto ctx = framework().context(); + + class Cmp {}; + + celix::ComponentManager<Cmp> cmpMng{ctx, std::make_shared<Cmp>()}; + EXPECT_FALSE(cmpMng.isEnabled()); + EXPECT_TRUE(cmpMng.isResolved()); //no deps -> resolved + EXPECT_EQ(cmpMng.getState(), celix::ComponentState::Disabled); + + cmpMng.enable(); + EXPECT_TRUE(cmpMng.isEnabled()); +} + +TEST_F(ComponentManagerTest, AddSvcDep) { + auto ctx = framework().context(); + + class Cmp {}; + class ISvc {}; + + celix::ComponentManager<Cmp> cmpMng{ctx, std::make_shared<Cmp>()}; + cmpMng.addServiceDependency(); //TODO + cmpMng.enable(); + EXPECT_TRUE(cmpMng.isEnabled()); + + +} \ No newline at end of file diff --git a/libs/framework_cxx/include/celix/ComponentManager.h b/libs/framework_cxx/include/celix/ComponentManager.h new file mode 100644 index 0000000..431b784 --- /dev/null +++ b/libs/framework_cxx/include/celix/ComponentManager.h @@ -0,0 +1,288 @@ +/** + *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 CXX_CELIX_COMPONENT_MANAGER_H +#define CXX_CELIX_COMPONENT_MANAGER_H + +#include <memory> +#include <unordered_map> + +#include "celix/BundleContext.h" + +namespace celix { + + enum class ComponentState { + Disabled, + Uninitialized, + Initialized, + Started + }; + + template<typename T, typename I> + class ServiceDependency; //forward declaration + class GenericServiceDependency; //forward declaration + + class GenericComponentManager { + public: + virtual ~GenericComponentManager() = default; + + ComponentState getState() const; + bool isEnabled() const; + bool isResolved() const; + + //TODO make friend of SvcDep + void updateState(); + protected: + GenericComponentManager(std::shared_ptr<BundleContext> _ctx, std::shared_ptr<void> cmpInstance) : ctx{std::move(_ctx)}, instance{std::move(cmpInstance)} {}; + + void removeServiceDependency(long serviceDependencyId); + void setEnabled(bool enable); + + + /**** Fields ****/ + mutable std::mutex mutex{}; //protects below + ComponentState state = ComponentState::Disabled; + bool enabled = false; + std::function<void()> init{}; + std::function<void()> start{}; + std::function<void()> stop{}; + std::function<void()> deinit{}; + + std::shared_ptr<BundleContext> ctx; + std::shared_ptr<void> instance; + + long nextServiceDependencyId = 1L; + std::unordered_map<long,GenericServiceDependency> serviceDependencies{}; + }; + + template<typename T> + class ComponentManager : public GenericComponentManager { + public: + using ComponentType = T; + + ComponentManager(std::shared_ptr<BundleContext> ctx, std::shared_ptr<T> cmpInstance); + ~ComponentManager() override = default; + + ComponentManager<T>& enable(); + ComponentManager<T>& disable(); + + ComponentManager<T>& setCallbacks( + void (T::*init)(), + void (T::*start)(), + void (T::*stop)(), + void (T::*deinit)()); + +// template<typename I> +// ServiceDependency<T,I>& addServiceDependency(); +// +// template<typename F> +// ServiceDependency<T,F>& addFunctionServiceDependency(const std::string &functionName); + + }; + + enum class Cardinality { + One, + Many + }; + + class GenericServiceDependency { + public: + virtual ~GenericServiceDependency() = default; + + bool isResolved() const; + Cardinality getCardinality() const; + bool getRequired() const; + const std::string& getFilter() const; + + protected: + GenericServiceDependency(std::shared_ptr<BundleContext> ctx); + + //Fields + mutable std::mutex mutex{}; //protects below + bool required = false; + std::string filter{}; + Cardinality cardinality = Cardinality::One; + std::vector<ServiceTracker> tracker{}; //max 1 + + std::shared_ptr<BundleContext> ctx; + }; + + template<typename T, typename I> + class ServiceDependency : public GenericServiceDependency { + public: + using ComponentType = T; + using ServiceType = I; + + ServiceDependency(std::shared_ptr<ComponentManager<T>> mng); + ~ServiceDependency() override = default; + + ServiceDependency<T,I>& setFilter(const std::string &filter); + ServiceDependency<T,I>& setRequired(bool required); + ServiceDependency<T,I>& setCardinality(celix::Cardinality cardinality); + ServiceDependency<T,I>& setCallbacks(void (T::*set)(std::shared_ptr<I>)); + ServiceDependency<T,I>& setCallbacks(void (T::*add)(std::shared_ptr<I>), void (T::*remove)(std::shared_ptr<I>)); + //TODO update callback + + void enable(); + void disable(); + bool isResolved(); + private: + std::function<void(std::shared_ptr<I>)> set{}; + std::function<void(std::shared_ptr<I>)> add{}; + std::function<void(std::shared_ptr<I>)> rem{}; + std::shared_ptr<ComponentManager<T>> mng; + }; +} + + + +/**************************** IMPLEMENTATION *************************************************************************/ + +template<typename T> +celix::ComponentManager<T>::ComponentManager(std::shared_ptr<BundleContext> ctx, std::shared_ptr<T> cmpInstance) : + GenericComponentManager(ctx, std::static_pointer_cast<void>(cmpInstance)) {} + +template<typename T> +celix::ComponentManager<T>& celix::ComponentManager<T>::enable() { + this->setEnabled(true); + return *this; +} + +template<typename T> +celix::ComponentManager<T>& celix::ComponentManager<T>::disable() { + this->setEnabled(false); + return *this; +} + +template<typename T> +celix::ComponentManager<T>& celix::ComponentManager<T>::setCallbacks( + void (T::*init)(), + void (T::*start)(), + void (T::*stop)(), + void (T::*deinit)()) { + std::lock_guard<std::mutex> lck{mutex}; + auto initFunc = [this,init]() { + if (init) { + (this->instance->*init)(); + } + }; + auto startFunc = [this,start]() { + if (start) { + (this->instance->*start)(); + } + }; + auto stopFunc = [this,stop]() { + if (stop) { + (this->instance->*stop)(); + } + }; + auto deinitFunc = [this,deinit]() { + if (deinit) { + (this->instance->*deinit)(); + } + }; + +} + +template<typename T, typename I> +celix::ServiceDependency<T,I>::ServiceDependency(std::shared_ptr<ComponentManager<T>> _mng) : mng{std::move(_mng)} { + add = [this](std::shared_ptr<I>) { + mng->updateState(); + }; + rem = [this](std::shared_ptr<I>) { + mng->updateState(); + }; +} + +template<typename T, typename I> +void celix::ServiceDependency<T,I>::enable() { + std::vector<celix::ServiceTracker> localTrackers{}; + { + std::lock_guard<std::mutex> lck{mutex}; + std::swap(localTrackers, tracker); + assert(tracker.size() == 0); + + ServiceTrackerOptions<I> opts{}; + opts.filter = this->filter; + opts.set = set; + opts.add = add; + opts.remove = rem; + + this->tracker.push_back(this->ctx->trackServices(opts)); + } + +} + +template<typename T, typename I> +void celix::ServiceDependency<T,I>::disable() { + std::vector<celix::ServiceTracker> localTrackers{}; + { + std::lock_guard<std::mutex> lck{mutex}; + std::swap(localTrackers, tracker); + assert(tracker.size() == 0); + } +} + + +template<typename T, typename I> +celix::ServiceDependency<T,I>& celix::ServiceDependency<T,I>::setCallbacks(void (T::*setfp)(std::shared_ptr<I>)) { + std::lock_guard<std::mutex> lck{mutex}; + set = [this, setfp](std::shared_ptr<I> svc) { + (mng->instance->*setfp)(svc); + }; + return *this; +} + +template<typename T, typename I> +celix::ServiceDependency<T,I>& celix::ServiceDependency<T,I>::setCallbacks(void (T::*addfp)(std::shared_ptr<I>), void (T::*remfp)(std::shared_ptr<I>)) { + std::lock_guard<std::mutex> lck{mutex}; + add = [this, addfp](std::shared_ptr<I> svc) { + (mng->instance->*addfp)(svc); + mng->updateState(); + }; + rem = [this, remfp](std::shared_ptr<I> svc) { + (mng->instance->*remfp)(svc); + mng->updateState(); + }; + return *this; +} + +template<typename T, typename I> +celix::ServiceDependency<T,I>& celix::ServiceDependency<T,I>::setFilter(const std::string &f) { + std::lock_guard<std::mutex> lck{mutex}; + filter = f; + return *this; +} + +template<typename T, typename I> +celix::ServiceDependency<T,I>& celix::ServiceDependency<T,I>::setRequired(bool r) { + std::lock_guard<std::mutex> lck{mutex}; + required = r; + return *this; +} + +template<typename T, typename I> +celix::ServiceDependency<T,I>& celix::ServiceDependency<T,I>::setCardinality(celix::Cardinality c) { + std::lock_guard<std::mutex> lck{mutex}; + cardinality = c; + return *this; +} + + +#endif //CXX_CELIX_PROPERTIES_H diff --git a/libs/framework_cxx/include/celix/Framework.h b/libs/framework_cxx/include/celix/Framework.h index 8689be7..8e7fdd7 100644 --- a/libs/framework_cxx/include/celix/Framework.h +++ b/libs/framework_cxx/include/celix/Framework.h @@ -88,7 +88,7 @@ namespace celix { std::string cacheDir() const; std::string uuid() const; - celix::BundleContext& context() const; + std::shared_ptr<celix::BundleContext> context() const; //TODO trackBundles diff --git a/libs/framework_cxx/src/ComponentManager.cc b/libs/framework_cxx/src/ComponentManager.cc new file mode 100644 index 0000000..b3b4205 --- /dev/null +++ b/libs/framework_cxx/src/ComponentManager.cc @@ -0,0 +1,102 @@ +/** + *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. + */ + + +#include <iostream> + +#include "celix/ComponentManager.h" + +celix::GenericServiceDependency::GenericServiceDependency(std::shared_ptr<celix::BundleContext> _ctx) : ctx{_ctx} {} + + +bool celix::GenericServiceDependency::isResolved() const { + std::lock_guard<std::mutex> lck{mutex}; + if (!tracker.empty()) { + return tracker[0].trackCount() >= 1; + } else { + return false; + } +} + +celix::Cardinality celix::GenericServiceDependency::getCardinality() const { + std::lock_guard<std::mutex> lck{mutex}; + return cardinality; +} + +bool celix::GenericServiceDependency::getRequired() const { + std::lock_guard<std::mutex> lck{mutex}; + return required; +} + +const std::string &celix::GenericServiceDependency::getFilter() const { + std::lock_guard<std::mutex> lck{mutex}; + return filter; +} + +celix::ComponentState celix::GenericComponentManager::getState() const { + std::lock_guard<std::mutex> lck{mutex}; + return state; +} + +bool celix::GenericComponentManager::isEnabled() const { + std::lock_guard<std::mutex> lck{mutex}; + //TODO update state + return enabled; +} + +bool celix::GenericComponentManager::isResolved() const { + std::lock_guard<std::mutex> lck{mutex}; +// for (auto &pair : serviceDependencies) { +// if (!pair->second.isResolved()) { +// return false; +// } +// } + return true; +} + +void celix::GenericComponentManager::removeServiceDependency(long serviceDependencyId) { + std::lock_guard<std::mutex> lck{mutex}; + serviceDependencies.erase(serviceDependencyId); +} + +void celix::GenericComponentManager::setEnabled(bool e) { + std::lock_guard<std::mutex> lck{mutex}; + enabled = e; +} + +void celix::GenericComponentManager::updateState() { + std::lock_guard<std::mutex> lck{mutex}; + + //TODO state thing + + //TODO check enabled. + + bool allDependenciesResolved = true; + for (auto &pair : serviceDependencies) { + if (!pair.second.isResolved()) { + allDependenciesResolved = false; + break; + } + } + + std::cout << "resolved? " << (allDependenciesResolved ? "true" : "false") << std::endl; +} + + + diff --git a/libs/framework_cxx/src/Framework.cc b/libs/framework_cxx/src/Framework.cc index 9128c0e..c5eb0ac 100644 --- a/libs/framework_cxx/src/Framework.cc +++ b/libs/framework_cxx/src/Framework.cc @@ -301,9 +301,9 @@ public: return fwUUID; } - celix::BundleContext& frameworkContext() const { + std::shared_ptr<celix::BundleContext> frameworkContext() const { std::lock_guard<std::mutex> lck(bundles.mutex); - return *bundles.entries.at(celix::FRAMEWORK_BUNDLE_ID)->context(); + return bundles.entries.at(celix::FRAMEWORK_BUNDLE_ID)->context(); } bool startFramework() { @@ -447,7 +447,7 @@ std::string celix::Framework::uuid() const { return pimpl->uuid(); } -celix::BundleContext& celix::Framework::context() const { +std::shared_ptr<celix::BundleContext> celix::Framework::context() const { return pimpl->frameworkContext(); } diff --git a/libs/registry/include/celix/Properties.h b/libs/registry/include/celix/Properties.h index 649cf4c..0588f3f 100644 --- a/libs/registry/include/celix/Properties.h +++ b/libs/registry/include/celix/Properties.h @@ -29,33 +29,53 @@ namespace celix { inline const std::string& getProperty(const Properties& props, const std::string &key, const std::string &defaultValue) noexcept { auto it = props.find(key); - if (it != props.end()) { - return props.at(key); - } else { - return defaultValue; - } + return it != props.end() ? it->second : defaultValue; } - inline int getProperty(const Properties& props, const std::string &key, int defaultValue) noexcept { - std::string val = getProperty(props, key, std::to_string(defaultValue)); - return std::stoi(val, nullptr, 10); + inline long getPropertyAsLong(const Properties& props, const std::string &key, long defaultValue) noexcept { + const std::string &strVal = getProperty(props, key, std::to_string(defaultValue)); + char *endptr[1]; + long lval = strtol(strVal.c_str(), endptr, 10); + return strVal.c_str() == *endptr ? defaultValue : lval; } - inline unsigned int getProperty(const Properties& props, const std::string &key, unsigned int defaultValue) noexcept { - std::string val = getProperty(props, key, std::to_string(defaultValue)); - return static_cast<unsigned int>(std::stoul(val, nullptr, 10)); //NOTE no std::stou ?? + inline unsigned long getPropertyAsUnsignedLong(const Properties& props, const std::string &key, unsigned long defaultValue) noexcept { + const std::string &strVal = getProperty(props, key, std::to_string(defaultValue)); + char *endptr[1]; + unsigned long lval = strtoul(strVal.c_str(), endptr, 10); + return strVal.c_str() == *endptr ? defaultValue : lval; } - inline long getProperty(const Properties& props, const std::string &key, long defaultValue) noexcept { - std::string val = getProperty(props, key, std::to_string(defaultValue)); - return std::stol(val, nullptr, 10); + inline int getPropertyAsInt(const Properties& props, const std::string &key, int defaultValue) noexcept { + long def = defaultValue; + long r = getPropertyAsLong(props, key, def); + return (int)r; } - inline unsigned int getProperty(const Properties& props, const std::string &key, unsigned long defaultValue) noexcept { - std::string val = getProperty(props, key, std::to_string(defaultValue)); - return std::stoul(val, nullptr, 10); + inline unsigned int getPropertyAsUnsignedInt(const Properties& props, const std::string &key, unsigned int defaultValue) noexcept { + unsigned long def = defaultValue; + unsigned long r = getPropertyAsUnsignedLong(props, key, def); + return (unsigned int)r; } + inline bool getPropertyAsBool(const Properties& props, const std::string &key, bool defaultValue) noexcept { + const std::string &strVal = getProperty(props, key, std::to_string(defaultValue)); + return strVal == "true" || strVal == "TRUE"; + } + + inline double getPropertyAsDouble(const Properties& props, const std::string &key, double defaultValue) noexcept { + const std::string &strVal = getProperty(props, key, std::to_string(defaultValue)); + char *endptr[1]; + double dval = strtod(strVal.c_str(), endptr); + return strVal.c_str() == *endptr ? defaultValue : dval; + } + + inline float getPropertyAsFloat(const Properties& props, const std::string &key, float defaultValue) noexcept { + const std::string &strVal = getProperty(props, key, std::to_string(defaultValue)); + char *endptr[1]; + float fval = strtof(strVal.c_str(), endptr); + return strVal.c_str() == *endptr ? defaultValue : fval; + } celix::Properties loadProperties(const std::string &path); celix::Properties loadProperties(std::istream &stream); diff --git a/libs/registry/src/ServiceRegistry.cc b/libs/registry/src/ServiceRegistry.cc index 33b2bb6..a8cf2f5 100644 --- a/libs/registry/src/ServiceRegistry.cc +++ b/libs/registry/src/ServiceRegistry.cc @@ -67,7 +67,7 @@ namespace { celix::Properties &&_props) : owner{std::move(_owner)}, svcId{_svcId}, svcName{std::move(_svcName)}, props{std::forward<celix::Properties>(_props)}, - ranking{celix::getProperty(props, celix::SERVICE_RANKING, 0L)}, + ranking{celix::getPropertyAsLong(props, celix::SERVICE_RANKING, 0L)}, svc{_svc}, svcFactory{_svcFac} {} SvcEntry(SvcEntry &&rhs) = delete;
