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
The following commit(s) were added to refs/heads/feature/cxx by this push: new 69e1792 CELIX-370: Improves state handling for component manager and adds support for provided services for the component manager 69e1792 is described below commit 69e179241b953b8b942d894dd5dada4119120419 Author: Pepijn Noltes <pepijnnol...@gmail.com> AuthorDate: Mon Aug 26 23:51:28 2019 +0200 CELIX-370: Improves state handling for component manager and adds support for provided services for the component manager --- .../gtest/src/ComponentManager_tests.cc | 225 ++++++++++- libs/framework_cxx/include/celix/BundleContext.h | 4 +- .../framework_cxx/include/celix/ComponentManager.h | 442 ++++++++++++++++----- libs/framework_cxx/src/BundleContext.cc | 2 +- libs/framework_cxx/src/ComponentManager.cc | 431 +++++++++++++++----- libs/registry/include/celix/Constants.h | 10 +- libs/registry/include/celix/ServiceRegistry.h | 99 +++-- libs/registry/src/ServiceRegistry.cc | 97 +++-- 8 files changed, 996 insertions(+), 314 deletions(-) diff --git a/libs/framework_cxx/gtest/src/ComponentManager_tests.cc b/libs/framework_cxx/gtest/src/ComponentManager_tests.cc index db7fc7e..7a0d3b1 100644 --- a/libs/framework_cxx/gtest/src/ComponentManager_tests.cc +++ b/libs/framework_cxx/gtest/src/ComponentManager_tests.cc @@ -32,37 +32,38 @@ private: }; +class Cmp {}; + +class ISvc {}; + + 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); + EXPECT_FALSE(cmpMng.isResolved()); //disabled -> not resolved + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::Disabled); cmpMng.enable(); EXPECT_TRUE(cmpMng.isEnabled()); - EXPECT_EQ(cmpMng.getState(), celix::ComponentState::Started); + EXPECT_TRUE(cmpMng.isResolved()); //no deps -> resolved + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentStarted); cmpMng.disable(); EXPECT_FALSE(cmpMng.isEnabled()); - EXPECT_EQ(cmpMng.getState(), celix::ComponentState::Disabled); + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::Disabled); } TEST_F(ComponentManagerTest, AddSvcDep) { auto ctx = framework().context(); - class Cmp {}; - class ISvc {}; - celix::ComponentManager<Cmp> cmpMng{ctx, std::make_shared<Cmp>()}; cmpMng.addServiceDependency<ISvc>() .setRequired(true); cmpMng.enable(); EXPECT_TRUE(cmpMng.isEnabled()); - EXPECT_EQ(cmpMng.getState(), celix::ComponentState::Uninitialized); + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentUninitialized); //dep not available -> cmp manager not resolved @@ -72,28 +73,38 @@ TEST_F(ComponentManagerTest, AddSvcDep) { auto svcReg = ctx->registerService(std::make_shared<ISvc>()); //dep available -> cmp manager resolved EXPECT_TRUE(cmpMng.isResolved()); - EXPECT_EQ(cmpMng.getState(), celix::ComponentState::Started); + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentStarted); cmpMng.disable(); //cmp disabled -> not resolved EXPECT_FALSE(cmpMng.isResolved()); - EXPECT_EQ(cmpMng.getState(), celix::ComponentState::Disabled); + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::Disabled); //dmp enabled & svc available -> resolved. cmpMng.enable(); EXPECT_TRUE(cmpMng.isResolved()); - EXPECT_EQ(cmpMng.getState(), celix::ComponentState::Started); + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentStarted); svcReg.unregister(); - EXPECT_EQ(cmpMng.getState(), celix::ComponentState::Initialized); + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentInitialized); //dep unregisted -> state is Initialized } -TEST_F(ComponentManagerTest, AddAndRemoveSvcDep) { +TEST_F(ComponentManagerTest, FindServiceDepenencies) { auto ctx = framework().context(); + std::string svcDepUuid; - class Cmp {}; - class ISvc {}; + celix::ComponentManager<Cmp> cmpMng{ctx, std::make_shared<Cmp>()}; + cmpMng.addServiceDependency<ISvc>() + .extractUUID(svcDepUuid); + + EXPECT_TRUE(cmpMng.findServiceDependency<ISvc>(svcDepUuid).isValid()); + EXPECT_FALSE(cmpMng.findServiceDependency<Cmp /*in correct template*/>(svcDepUuid).isValid()); + EXPECT_FALSE(cmpMng.findServiceDependency<ISvc>("non existing uuid").isValid()); +} + +TEST_F(ComponentManagerTest, AddAndRemoveSvcDep) { + auto ctx = framework().context(); std::string svcDepUuid; celix::ComponentManager<Cmp> cmpMng{ctx, std::make_shared<Cmp>()}; @@ -109,4 +120,182 @@ TEST_F(ComponentManagerTest, AddAndRemoveSvcDep) { cmpMng.removeServiceDependency(svcDepUuid); //dep removed -> cmp manager resolved EXPECT_TRUE(cmpMng.isResolved()); -} \ No newline at end of file +} + + +TEST_F(ComponentManagerTest, AddAndEnableSvcDep) { + auto ctx = framework().context(); + + celix::ComponentManager<Cmp> cmpMng{ctx, std::make_shared<Cmp>()}; + cmpMng.addServiceDependency<ISvc>() + .setRequired(true); + cmpMng.enable(); + EXPECT_TRUE(cmpMng.isEnabled()); + EXPECT_EQ(cmpMng.nrOfServiceDependencies(), 1); + + //dep not available -> cmp manager not resolved + EXPECT_FALSE(cmpMng.isResolved()); + + auto svcReg = ctx->registerService(std::make_shared<ISvc>()); + //svc add -> cmp resolved + EXPECT_TRUE(cmpMng.isResolved()); + + std::string svcUUID{}; + cmpMng.addServiceDependency<ISvc>() + .setFilter("(non_existing=*)") + .extractUUID(svcUUID); + EXPECT_EQ(cmpMng.nrOfServiceDependencies(), 2); + //new dep is still disabled -> cmp is resolved + EXPECT_TRUE(cmpMng.isResolved()); + + cmpMng.findServiceDependency<ISvc>(svcUUID).enable(); + //new required svc dep -> cmp not resolved + EXPECT_FALSE(cmpMng.isResolved()); + //cmp already started should now be initialized + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentInitialized); + EXPECT_EQ(cmpMng.nrOfServiceDependencies(), 2); + + cmpMng.removeServiceDependency(svcUUID); + //required svc dep is gone -> resolved + EXPECT_TRUE(cmpMng.isResolved()); + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentStarted); + EXPECT_EQ(cmpMng.nrOfServiceDependencies(), 1); +} + +TEST_F(ComponentManagerTest, CmpLifecycleCallbacks) { + class TestCmp { + public: + int initCount = 0; + int startCount = 0; + int stopCount = 0; + int deinitCount = 0; + + void init() {++initCount;} + void start() {++startCount;} + void stop() {++stopCount;} + void deinit() {++deinitCount;} + }; + + auto ctx = framework().context(); + + celix::ComponentManager<TestCmp> cmpMng{ctx, std::make_shared<TestCmp>()}; + cmpMng.setCallbacks(&TestCmp::init, &TestCmp::start, &TestCmp::stop, &TestCmp::deinit); + auto cmp = cmpMng.getCmpInstance(); + EXPECT_EQ(0, cmp->initCount); + EXPECT_EQ(0, cmp->startCount); + EXPECT_EQ(0, cmp->stopCount); + EXPECT_EQ(0, cmp->deinitCount); + + cmpMng.enable(); // started + EXPECT_EQ(1, cmp->initCount); + EXPECT_EQ(1, cmp->startCount); + EXPECT_EQ(0, cmp->stopCount); + EXPECT_EQ(0, cmp->deinitCount); + + cmpMng.disable(); // disbabling -> stop -> deinit + EXPECT_EQ(1, cmp->initCount); + EXPECT_EQ(1, cmp->startCount); + EXPECT_EQ(1, cmp->stopCount); + EXPECT_EQ(1, cmp->deinitCount); +} + +TEST_F(ComponentManagerTest, SuspenseAndLockingStrategy) { + auto ctx = framework().context(); + + celix::ComponentManager<Cmp> cmpMng{ctx, std::make_shared<Cmp>()}; + cmpMng.addServiceDependency<ISvc>().setFilter("(id=locking)").setRequired(false).setStrategy(celix::UpdateServiceStrategy::Locking); + cmpMng.addServiceDependency<ISvc>().setFilter("(id=suspense)").setRequired(false); + cmpMng.enable(); //no required dep -> cmp started. + + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentStarted); + + celix::Properties props{}; + props["id"] = "locking"; + auto reg1 = ctx->registerService(std::make_shared<ISvc>(), props); //svc registered -> cmp updated, using locking strategy so no suspense triggered. + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentStarted); + EXPECT_EQ(cmpMng.getSuspendedCount(), 0); //no suspense needed; + + + props["id"] = "suspense"; + auto reg2 = ctx->registerService(std::make_shared<ISvc>(), props); //svc registered -> cmp updated, using suspense strategy so 1 suspense triggered. + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentStarted); + EXPECT_EQ(cmpMng.getSuspendedCount(), 1); //2x suspense needed (for both services); + + reg2.unregister(); + EXPECT_EQ(cmpMng.getSuspendedCount(), 2); +} + +TEST_F(ComponentManagerTest, RequiredAndOptionalDependencies) { + auto ctx = framework().context(); + + celix::ComponentManager<Cmp> cmpMng{ctx, std::make_shared<Cmp>()}; + cmpMng.addServiceDependency<ISvc>().setFilter("(id=required)").setRequired(true); + cmpMng.addServiceDependency<ISvc>().setFilter("(id=optional)").setRequired(false); + cmpMng.enable(); //required dep -> cmp uninitialized. + + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentUninitialized); + + celix::Properties props{}; + props["id"] = "optional"; + auto reg1 = ctx->registerService(std::make_shared<ISvc>(), props); + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentUninitialized); + + + props["id"] = "required"; + auto reg2 = ctx->registerService(std::make_shared<ISvc>(), props); + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentStarted); + + cmpMng.addServiceDependency<ISvc>().setFilter("(id=optional2)").setRequired(false).enable(); + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentStarted); + + cmpMng.addServiceDependency<ISvc>().setFilter("(id=required2)").setRequired(true).enable(); + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentInitialized); + + props["id"] = "required2"; + auto reg3 = ctx->registerService(std::make_shared<ISvc>(), props); //missing required cmp from started to initialized. + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentStarted); +} + +TEST_F(ComponentManagerTest, AddProvidedServices) { + class TestCmp : public ISvc {}; + auto ctx = framework().context(); + celix::ComponentManager<TestCmp> cmpMng{ctx, std::make_shared<TestCmp>()}; + cmpMng.addProvidedService<ISvc>().addProperty("nr", "1"); + cmpMng.enable(); + EXPECT_EQ(cmpMng.getState(), celix::ComponentManagerState::ComponentStarted); + EXPECT_EQ(cmpMng.nrOfProvidedServices(), 1); + + //one service should be registered + EXPECT_EQ(ctx->findServices<ISvc>().size(), 1); + EXPECT_EQ(ctx->findServices<ISvc>("(nr=1)").size(), 1); + + std::string provideUUID; + cmpMng.addProvidedService<ISvc>().addProperty("nr", "2").extractUUID(provideUUID); + //still only one -> second provide not jet enabled + EXPECT_EQ(ctx->findServices<ISvc>().size(), 1); + EXPECT_EQ(cmpMng.nrOfProvidedServices(), 2); + + cmpMng.findProvidedService<ISvc>(provideUUID).enable(); + //enabled -> should be 2 now + EXPECT_EQ(ctx->findServices<ISvc>().size(), 2); + cmpMng.findProvidedService<ISvc>(provideUUID).disable(); + //disabled -> should be 1 now + EXPECT_EQ(ctx->findServices<ISvc>().size(), 1); + cmpMng.findProvidedService<ISvc>(provideUUID).enable(); + cmpMng.removeProvideService(provideUUID); + EXPECT_EQ(cmpMng.nrOfProvidedServices(), 1); +} + +TEST_F(ComponentManagerTest, FindProvidedServices) { + class TestCmp : public ISvc {}; + auto ctx = framework().context(); + celix::ComponentManager<TestCmp> cmpMng{ctx, std::make_shared<TestCmp>()}; + + std::string uuid; + cmpMng.addProvidedService<ISvc>().extractUUID(uuid); + + EXPECT_TRUE(cmpMng.findProvidedService<ISvc>(uuid).isValid()); + EXPECT_FALSE(cmpMng.findProvidedService<ISvc>("bla").isValid()); +} + +//TODO test callbacks svc dep (set, add, rem, update) diff --git a/libs/framework_cxx/include/celix/BundleContext.h b/libs/framework_cxx/include/celix/BundleContext.h index 2cdbf97..ef6f5d1 100644 --- a/libs/framework_cxx/include/celix/BundleContext.h +++ b/libs/framework_cxx/include/celix/BundleContext.h @@ -39,8 +39,8 @@ namespace celix { std::shared_ptr<celix::IBundle> bundle() const; template<typename I> - celix::ServiceRegistration registerService(I &svc, celix::Properties props = {}) { - return registry().registerService<I>(svc, std::move(props), bundle()); + celix::ServiceRegistration registerService(I &&svc, celix::Properties props = {}) { + return registry().registerService<I>(std::forward<I>(svc), std::move(props), bundle()); } template<typename I> diff --git a/libs/framework_cxx/include/celix/ComponentManager.h b/libs/framework_cxx/include/celix/ComponentManager.h index 2612fa5..0e0183a 100644 --- a/libs/framework_cxx/include/celix/ComponentManager.h +++ b/libs/framework_cxx/include/celix/ComponentManager.h @@ -29,35 +29,47 @@ namespace celix { - enum class ComponentState { + enum class ComponentManagerState { Disabled, - Uninitialized, - Initialized, - Started + ComponentUninitialized, + ComponentInitialized, + ComponentStarted }; template<typename T, typename I> class ServiceDependency; //forward declaration class GenericServiceDependency; //forward declaration + template<typename T, typename I> + class ProvidedService; //forward declaration + class GenericProvidedService; //forward declaration + class GenericComponentManager { public: virtual ~GenericComponentManager() = default; - ComponentState getState() const; + ComponentManagerState getState() const; bool isEnabled() const; bool isResolved() const; std::string getName() const; std::string getUUD() const; + std::size_t getSuspendedCount() const; - //TODO make friend of SvcDep - void updateState(); void removeServiceDependency(const std::string& serviceDependencyUUID); + void removeProvideService(const std::string& provideServiceUUID); + std::size_t nrOfServiceDependencies(); + std::size_t nrOfProvidedServices(); protected: - GenericComponentManager(std::shared_ptr<BundleContext> _ctx, const std::string &_name); + GenericComponentManager(std::shared_ptr<BundleContext> ctx, const std::string &name); void setEnabled(bool enable); + std::shared_ptr<GenericServiceDependency> findGenericServiceDependency(const std::string& svcName, const std::string& svcDepUUID); + std::shared_ptr<GenericProvidedService> findGenericProvidedService(const std::string& svcName, const std::string& providedServiceUUID); + void updateState(); + void updateServiceRegistrations(); + void suspense(); + void resume(); /**** Fields ****/ const std::shared_ptr<BundleContext> ctx; @@ -65,23 +77,28 @@ namespace celix { const std::string uuid; std::mutex callbacksMutex{}; //protects below std::functions - std::function<void()> init{[]{/*nop*/}}; - std::function<void()> start{[]{/*nop*/}}; - std::function<void()> stop{[]{/*nop*/}}; - std::function<void()> deinit{[]{/*nop*/}}; + std::function<void()> initCmp{[]{/*nop*/}}; + std::function<void()> startCmp{[]{/*nop*/}}; + std::function<void()> stopCmp{[]{/*nop*/}}; + std::function<void()> deinitCmp{[]{/*nop*/}}; std::mutex serviceDependenciesMutex{}; std::unordered_map<std::string,std::shared_ptr<GenericServiceDependency>> serviceDependencies{}; //key = dep uuid + + std::mutex providedServicesMutex{}; + std::unordered_map<std::string,std::shared_ptr<GenericProvidedService>> providedServices{}; //key = provide uuid private: - void setState(ComponentState state); + void setState(ComponentManagerState state); void setInitialized(bool initialized); void transition(); mutable std::mutex stateMutex{}; //protects below - ComponentState state = ComponentState::Disabled; + ComponentManagerState state = ComponentManagerState::Disabled; bool enabled = false; bool initialized = false; - std::queue<std::pair<ComponentState,ComponentState>> transitionQueue{}; + bool suspended = false; + std::size_t suspendedCount = 0; + std::queue<std::pair<ComponentManagerState,ComponentManagerState>> transitionQueue{}; }; template<typename T> @@ -95,8 +112,6 @@ namespace celix { ComponentManager<T>& enable(); ComponentManager<T>& disable(); - std::shared_ptr<T> instance(); - ComponentManager<T>& setCallbacks( void (T::*init)(), void (T::*start)(), @@ -107,14 +122,22 @@ namespace celix { ServiceDependency<T,I>& addServiceDependency(); template<typename I> - ServiceDependency<T,I>* findServiceDependency(const std::string& serviceDependencyUUID); + ServiceDependency<T,I>& findServiceDependency(const std::string& serviceDependencyUUID); ComponentManager<T>& extractUUID(std::string& out); + + template<typename I> + ProvidedService<T,I>& addProvidedService(); + + template<typename I> + ProvidedService<T,I>& findProvidedService(const std::string& providedServiceUUID); // // template<typename F> // ServiceDependency<T,F>& addFunctionServiceDependency(const std::string &functionName); + + std::shared_ptr<T> getCmpInstance() const; private: - std::shared_ptr<T> inst; + const std::shared_ptr<T> inst; }; @@ -123,30 +146,50 @@ namespace celix { Many }; + enum class UpdateServiceStrategy { + Suspense, + Locking + }; + class GenericServiceDependency { public: virtual ~GenericServiceDependency() = default; bool isResolved() const; Cardinality getCardinality() const; - bool getRequired() const; + bool isRequired() const; const std::string& getFilter() const; - std::string getUUD(); + const std::string& getUUD() const; + const std::string& getSvcName() const; + bool isValid() const; + UpdateServiceStrategy getStrategy() const; - bool isEnabled(); + bool isEnabled() const; virtual void setEnabled(bool e) = 0; protected: GenericServiceDependency( std::shared_ptr<BundleContext> ctx, - std::function<void()> stateChangedCallback); + std::string svcName, + std::function<void()> stateChangedCallback, + std::function<void()> suspenseCallback, + std::function<void()> resumeCallback, + bool isValid); + + void preServiceUpdate(); + void postServiceUpdate(); //Fields const std::shared_ptr<BundleContext> ctx; + const std::string svcName; const std::function<void()> stateChangedCallback; + const std::function<void()> suspenseCallback; + const std::function<void()> resumeCallback; const std::string uuid; + const bool valid; mutable std::mutex mutex{}; //protects below - bool required = false; + UpdateServiceStrategy strategy = UpdateServiceStrategy::Suspense; + bool required = true; std::string filter{}; Cardinality cardinality = Cardinality::One; std::vector<ServiceTracker> tracker{}; //max 1 (1 == enabled / 0 = disabled @@ -158,10 +201,17 @@ namespace celix { using ComponentType = T; using ServiceType = I; - ServiceDependency(std::shared_ptr<BundleContext> ctx, std::function<void()> stateChangedCallback, std::function<std::shared_ptr<T>()> getCmpInstance); + ServiceDependency( + std::shared_ptr<BundleContext> ctx, + std::function<void()> stateChangedCallback, + std::function<std::shared_ptr<T>()> getCmpInstance, + std::function<void()> suspenseCallback, + std::function<void()> resumeCallback, + bool isValid = true); ~ServiceDependency() override = default; ServiceDependency<T,I>& setFilter(const std::string &filter); + ServiceDependency<T,I>& setStrategy(UpdateServiceStrategy strategy); ServiceDependency<T,I>& setRequired(bool required); ServiceDependency<T,I>& setCardinality(celix::Cardinality cardinality); ServiceDependency<T,I>& setCallbacks(void (T::*set)(std::shared_ptr<I>)); @@ -169,12 +219,6 @@ namespace celix { //TODO update callback //TODO callbacks with properties and owner - ServiceDependency<T,I>& setFunctionCallbacks( - std::function<void(std::shared_ptr<I>, const celix::Properties &props, const celix::IResourceBundle &owner)> set, - std::function<void(std::shared_ptr<I>, const celix::Properties &props, const celix::IResourceBundle &owner)> add, - std::function<void(std::shared_ptr<I>, const celix::Properties &props, const celix::IResourceBundle &owner)> rem, - std::function<void(std::vector<std::tuple<std::shared_ptr<I>, const celix::Properties*, const celix::IResourceBundle *>> rankedServices)> update); - ServiceDependency<T,I>& extractUUID(std::string& out); ServiceDependency<T,I>& enable(); ServiceDependency<T,I>& disable(); @@ -182,21 +226,80 @@ namespace celix { void setEnabled(bool e) override; private: - const std::function<std::shared_ptr<T>()> getCmpInstance; + void setService(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner); + void addService(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner); + void remService(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner); + void updateServices(std::vector<std::tuple<std::shared_ptr<I>, const celix::Properties*, const celix::IResourceBundle *>> rankedServices); + const std::function<std::shared_ptr<T>()> getCmpInstance; + std::mutex callbacksMutex{}; //protects below std::function<void(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner)> set{}; std::function<void(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner)> add{}; std::function<void(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner)> rem{}; std::function<void(std::vector<std::tuple<std::shared_ptr<I>, const celix::Properties*, const celix::IResourceBundle *>> rankedServices)> update{}; }; + + + class GenericProvidedService { + public: + virtual ~GenericProvidedService() = default; + + bool isEnabled() const; + const std::string& getUUID() const; + const std::string& getServiceName() const; + bool isValid() const; + + void setEnabled(bool enabled); + void unregisterService(); + bool isServiceRegistered(); + virtual void registerService() = 0; + protected: + GenericProvidedService(std::shared_ptr<celix::BundleContext> ctx, std::string svcName, std::function<void()> updateServiceRegistrationsCallback, bool valid); + + const std::shared_ptr<celix::BundleContext> ctx; + const std::string uuid; + const std::string svcName; + const std::function<void()> updateServiceRegistrationsCallback; + const bool valid; + + mutable std::mutex mutex{}; //protects below + bool enabled = false; + std::vector<celix::ServiceRegistration> registration{}; //max size 1 (optional). 0 == disabled, 1 == enabled + celix::Properties properties{}; + }; + + template<typename T, typename I> + class ProvidedService : public GenericProvidedService { + public: + ProvidedService( + std::shared_ptr<celix::BundleContext> ctx, + std::function<std::shared_ptr<T>()> getCmpInstanceCallback, + std::function<void()> updateServiceRegistrationsCallback, + bool valid = true); + virtual ~ProvidedService() = default; + + ProvidedService& extractUUID(std::string& out); + ProvidedService& enable(); + ProvidedService& disable(); + ProvidedService& setProperties(const celix::Properties& props); + ProvidedService& setProperties(celix::Properties props); + ProvidedService& addProperty(const std::string &name, const std::string &val); //TODO add int, long, bool, etc property + + void registerService() override; + private: + const std::function<std::shared_ptr<T>()> getcmpInstanceCallback; + }; } -std::ostream& operator<< (std::ostream& out, celix::ComponentState state); +std::ostream& operator<< (std::ostream& out, celix::ComponentManagerState state); +std::ostream& operator<< (std::ostream& out, const celix::GenericComponentManager& mng); -/**************************** IMPLEMENTATION *************************************************************************/ +/********************************************************************************************************************** + ComponentManager Implementation + **********************************************************************************************************************/ template<typename T> celix::ComponentManager<T>::ComponentManager( @@ -224,35 +327,34 @@ celix::ComponentManager<T>& celix::ComponentManager<T>::setCallbacks( void (T::*memberStop)(), void (T::*memberDeinit)()) { std::lock_guard<std::mutex> lck{callbacksMutex}; - init = [this, memberInit]() { + initCmp = [this, memberInit]() { if (memberInit) { - (this->instance()->*memberInit)(); + T* ptr = this->getCmpInstance().get(); + (ptr->*memberInit)(); } }; - start = [this, memberStart]() { + startCmp = [this, memberStart]() { if (memberStart) { - (this->instance()->*memberStart)(); + T* ptr = this->getCmpInstance().get(); + (ptr->*memberStart)(); } }; - stop = [this, memberStop]() { + stopCmp = [this, memberStop]() { if (memberStop) { - (this->instance()->*memberStop)(); + T* ptr = this->getCmpInstance().get(); + (ptr->*memberStop)(); } }; - deinit = [this, memberDeinit]() { + deinitCmp = [this, memberDeinit]() { if (memberDeinit) { - (this->instance()->*memberDeinit)(); + T* ptr = this->getCmpInstance().get(); + (ptr->*memberDeinit)(); } }; return *this; } template<typename T> -std::shared_ptr<T> celix::ComponentManager<T>::instance() { - return inst; -} - -template<typename T> celix::ComponentManager<T>& celix::ComponentManager<T>::extractUUID(std::string &out) { out = uuid; return *this; @@ -261,7 +363,7 @@ celix::ComponentManager<T>& celix::ComponentManager<T>::extractUUID(std::string template<typename T> template<typename I> celix::ServiceDependency<T,I>& celix::ComponentManager<T>::addServiceDependency() { - auto *dep = new celix::ServiceDependency<T,I>{ctx, [this]{updateState();}, [this]{return instance();}}; + auto *dep = new celix::ServiceDependency<T,I>{ctx, [this]{updateState();}, [this]{return getCmpInstance();}, [this]{suspense();}, [this]{resume();}}; std::lock_guard<std::mutex> lck{serviceDependenciesMutex}; serviceDependencies[dep->getUUD()] = std::unique_ptr<GenericServiceDependency>{dep}; return *dep; @@ -269,52 +371,97 @@ celix::ServiceDependency<T,I>& celix::ComponentManager<T>::addServiceDependency( template<typename T> template<typename I> -celix::ServiceDependency<T,I>* celix::ComponentManager<T>::findServiceDependency(const std::string& serviceDependencyUUID) { - std::lock_guard<std::mutex> lck{serviceDependenciesMutex}; - auto it = serviceDependencies.find(serviceDependencyUUID); - return it != serviceDependencies.end() ? nullptr : it->second.get(); +celix::ServiceDependency<T,I>& celix::ComponentManager<T>::findServiceDependency(const std::string& serviceDependencyUUID) { + static celix::ServiceDependency<T,I> invalid{ctx, []{}, []{return nullptr;}, []{} , []{}, false}; + auto svcName = celix::typeName<I>(); + auto found = findGenericServiceDependency(svcName, serviceDependencyUUID); + if (found) { + auto *ptr = static_cast<celix::ServiceDependency<T,I>*>(found.get()); + return *ptr; + } else { + //note warning logged in findGenericServiceDependency + return invalid; + } } +template<typename T> +std::shared_ptr<T> celix::ComponentManager<T>::getCmpInstance() const { + return inst; +} +template<typename T> +template<typename I> +celix::ProvidedService<T, I> &celix::ComponentManager<T>::addProvidedService() { + auto *provided = new celix::ProvidedService<T,I>{ctx, [this]{return getCmpInstance();}, [this]{updateServiceRegistrations();}}; + std::lock_guard<std::mutex> lck{providedServicesMutex}; + providedServices[provided->getUUID()] = std::shared_ptr<GenericProvidedService>{provided}; + return *provided; +} +template<typename T> +template<typename I> +celix::ProvidedService<T, I> &celix::ComponentManager<T>::findProvidedService(const std::string &providedServiceUUID) { + static celix::ProvidedService<T,I> invalid{ctx, []{return nullptr;}, []{}, false}; + auto svcName = celix::typeName<I>(); + auto found = findGenericProvidedService(svcName, providedServiceUUID); + if (found) { + auto *ptr = static_cast<celix::ProvidedService<T,I>*>(found.get()); + return *ptr; + } else { + //note warning logged in findGenericProvidedService + return invalid; + } +} +/********************************************************************************************************************** + ServiceDependency Implementation + **********************************************************************************************************************/ template<typename T, typename I> celix::ServiceDependency<T,I>::ServiceDependency( std::shared_ptr<celix::BundleContext> _ctx, std::function<void()> _stateChangedCallback, - std::function<std::shared_ptr<T>()> _getCmpInstance) : GenericServiceDependency{std::move(_ctx), std::move(_stateChangedCallback)}, getCmpInstance{_getCmpInstance} {}; + std::function<std::shared_ptr<T>()> _getCmpInstance, + std::function<void()> _suspenseCallback, + std::function<void()> _resumeCallback, + bool isValid) : + GenericServiceDependency{std::move(_ctx), + celix::typeName<I>(), + std::move(_stateChangedCallback), + std::move(_suspenseCallback), + std::move(_resumeCallback), + isValid}, + getCmpInstance{std::move(_getCmpInstance)} {}; template<typename T, typename I> void celix::ServiceDependency<T,I>::setEnabled(bool enable) { bool currentlyEnabled = isEnabled(); - std::vector<ServiceTracker> newTracker{}; - if (enable && !currentlyEnabled) { - //enable - ServiceTrackerOptions<I> opts{}; - opts.filter = this->filter; - opts.setWithOwner = set; - opts.addWithOwner = add; - opts.removeWithOwner = rem; - opts.updateWithOwner = [this](std::vector<std::tuple<std::shared_ptr<I>, const celix::Properties*, const celix::IResourceBundle *>> rankedServices) { - if (this->update) { - //TODO lock? - this->update(std::move(rankedServices)); - } - this->stateChangedCallback(); - }; - newTracker.emplace_back(ctx->trackServices(opts)); - std::lock_guard<std::mutex> lck{mutex}; - std::swap(tracker, newTracker); - - } else if (!enable and currentlyEnabled) { - //disable - std::lock_guard<std::mutex> lck{mutex}; - std::swap(tracker, newTracker/*empty*/); + { + std::vector<ServiceTracker> newTracker{}; + if (enable && !currentlyEnabled) { + //enable + using namespace std::placeholders; + ServiceTrackerOptions<I> opts{}; + opts.filter = this->filter; + opts.preServiceUpdateHook = [this]{preServiceUpdate();}; + opts.postServiceUpdateHook = [this]{postServiceUpdate();}; + opts.setWithOwner = std::bind(&ServiceDependency::setService, this, _1, _2, _3); + opts.addWithOwner = std::bind(&ServiceDependency::addService, this, _1, _2, _3); + opts.removeWithOwner = std::bind(&ServiceDependency::remService, this, _1, _2, _3); + opts.updateWithOwner = std::bind(&ServiceDependency::updateServices, this, _1); + newTracker.emplace_back(ctx->trackServices(opts)); + std::lock_guard<std::mutex> lck{mutex}; + std::swap(tracker, newTracker); + + } else if (!enable and currentlyEnabled) { + //disable + std::lock_guard<std::mutex> lck{mutex}; + std::swap(tracker, newTracker/*empty*/); + } + //newTracker out of scope -> RAII -> for disable clear current tracker, for enable empty newTracker } - - //newTracker out of scope -> RAII -> for disable clear current tracker, for enable empty newTracker + stateChangedCallback(); } template<typename T, typename I> @@ -329,54 +476,28 @@ celix::ServiceDependency<T,I>& celix::ServiceDependency<T,I>::disable() { return *this; } -template<typename T, typename I> -celix::ServiceDependency<T,I>& celix::ServiceDependency<T,I>::setFunctionCallbacks( - std::function<void(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner)> setArg, - std::function<void(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner)> addArg, - std::function<void(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner)> remArg, - std::function<void(std::vector<std::tuple<std::shared_ptr<I>, const celix::Properties*, const celix::IResourceBundle *>> rankedServices)> updateArg) { - - //TODO lock or disable? - set = {}; - add = {}; - rem = {}; - update = {}; - - if (setArg) { - set = std::move(setArg); - } - if (addArg) { - add = addArg; - } - if (remArg) { - rem = remArg; - } - if (updateArg) { - update = std::move(updateArg); - } - return *this; -} 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}; auto setFunc = [this, setfp](std::shared_ptr<I> svc, const celix::Properties &, const celix::IResourceBundle &) { (getCmpInstance()->instance->*setfp)(svc); }; - return setFunctionCallbacks(std::move(setFunc), {}, {}, {}); + std::lock_guard<std::mutex> lck{callbacksMutex}; + set = std::move(setFunc); } 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}; auto addFunc = [this, addfp](std::shared_ptr<I> svc, const celix::Properties &, const celix::IResourceBundle &) { (getCmpInstance()->instance->*addfp)(svc); }; auto remFunc = [this, remfp](std::shared_ptr<I> svc, const celix::Properties &, const celix::IResourceBundle &) { (getCmpInstance()->instance->*remfp)(svc); }; - return setFunctionCallbacks({}, std::move(addFunc), std::move(remFunc), {}); + std::lock_guard<std::mutex> lck{callbacksMutex}; + add = std::move(addFunc); + rem = std::move(remFunc); } template<typename T, typename I> @@ -387,6 +508,13 @@ celix::ServiceDependency<T,I>& celix::ServiceDependency<T,I>::setFilter(const st } template<typename T, typename I> +celix::ServiceDependency<T,I>& celix::ServiceDependency<T,I>::setStrategy(UpdateServiceStrategy s) { + std::lock_guard<std::mutex> lck{mutex}; + strategy = s; + 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; @@ -406,5 +534,101 @@ celix::ServiceDependency<T,I>& celix::ServiceDependency<T,I>::extractUUID(std::s return *this; } +template<typename T, typename I> +void celix::ServiceDependency<T,I>::setService(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner) { + std::lock_guard<std::mutex> lck{callbacksMutex}; + if (set) { + set(std::move(svc), props, owner); + } +} + +template<typename T, typename I> +void celix::ServiceDependency<T,I>::addService(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner) { + std::lock_guard<std::mutex> lck{callbacksMutex}; + if (add) { + add(std::move(svc), props, owner); + } +} + +template<typename T, typename I> +void celix::ServiceDependency<T,I>::remService(std::shared_ptr<I> svc, const celix::Properties &props, const celix::IResourceBundle &owner) { + std::lock_guard<std::mutex> lck{callbacksMutex}; + if (rem) { + rem(std::move(svc), props, owner); + } +} + +template<typename T, typename I> +void celix::ServiceDependency<T,I>::updateServices(std::vector<std::tuple<std::shared_ptr<I>, const celix::Properties*, const celix::IResourceBundle *>> rankedServices) { + std::lock_guard<std::mutex> lck{callbacksMutex}; + if (update) { + update(std::move(rankedServices)); + } +} + +/********************************************************************************************************************** + ProvidedService Implementation + **********************************************************************************************************************/ + +template<typename T, typename I> +celix::ProvidedService<T,I>::ProvidedService( + std::shared_ptr<celix::BundleContext> ctx, + std::function<std::shared_ptr<T>()> _getCmpInstanceCallback, + std::function<void()> updateServiceRegistrationsCallback, + bool valid) : + GenericProvidedService{ctx, celix::serviceName<I>(), updateServiceRegistrationsCallback, valid}, getcmpInstanceCallback{_getCmpInstanceCallback} {} + +template<typename T, typename I> +void celix::ProvidedService<T, I>::registerService() { + if (isServiceRegistered()) { + return; //already registered. note that also means a disable -> enable is needed to update a provided service + } + auto inst = getcmpInstanceCallback(); + + //TODO try to improve compile error whent this happens + std::shared_ptr<I> svcPtr = inst; //NOTE T should implement (inherit) I + + std::vector<ServiceRegistration> newReg{}; + newReg.emplace_back(ctx->registerService<I>(inst, properties)); + std::lock_guard<std::mutex> lck{mutex}; + std::swap(newReg, registration); +} + +template<typename T, typename I> +celix::ProvidedService<T,I>& celix::ProvidedService<T, I>::extractUUID(std::string &out) { + out = uuid; + return *this; +} + +template<typename T, typename I> +celix::ProvidedService<T,I>& celix::ProvidedService<T, I>::enable() { + setEnabled(true); + return *this; +} + +template<typename T, typename I> +celix::ProvidedService<T,I>& celix::ProvidedService<T, I>::disable() { + setEnabled(false); + return *this; +} + +template<typename T, typename I> +celix::ProvidedService<T,I>& celix::ProvidedService<T, I>::setProperties(const celix::Properties& p) { + properties = p; + return *this; +} + +template<typename T, typename I> +celix::ProvidedService<T,I>& celix::ProvidedService<T, I>::setProperties(celix::Properties p) { + properties = std::move(p); + return *this; +} + +template<typename T, typename I> +celix::ProvidedService<T,I>& celix::ProvidedService<T, I>::addProperty(const std::string &name, const std::string &val) { + properties[name] = val; + return *this; +} + #endif //CXX_CELIX_COMPONENT_MANAGER_H diff --git a/libs/framework_cxx/src/BundleContext.cc b/libs/framework_cxx/src/BundleContext.cc index 045f2e3..0d9e50d 100644 --- a/libs/framework_cxx/src/BundleContext.cc +++ b/libs/framework_cxx/src/BundleContext.cc @@ -26,7 +26,7 @@ namespace celix { public: Impl(std::shared_ptr<celix::IBundle> _bnd) : bnd{std::move(_bnd)}, - reg{&bnd->framework().registry(celix::C_AND_CXX_LANG_REG)} {} + reg{&bnd->framework().registry("main")} {} Impl(const Impl&) = delete; Impl& operator=(const Impl&) = delete; diff --git a/libs/framework_cxx/src/ComponentManager.cc b/libs/framework_cxx/src/ComponentManager.cc index 07e3bc0..74af968 100644 --- a/libs/framework_cxx/src/ComponentManager.cc +++ b/libs/framework_cxx/src/ComponentManager.cc @@ -38,10 +38,18 @@ static std::string genUUID() { celix::GenericServiceDependency::GenericServiceDependency( std::shared_ptr<celix::BundleContext> _ctx, - std::function<void()> _stateChangedCallback) : + std::string _svcName, + std::function<void()> _stateChangedCallback, + std::function<void()> _suspendCallback, + std::function<void()> _resumeCallback, + bool _isValid) : ctx{std::move(_ctx)}, + svcName{std::move(_svcName)}, stateChangedCallback{std::move(_stateChangedCallback)}, - uuid{genUUID()} {} + suspenseCallback{std::move(_suspendCallback)}, + resumeCallback{std::move(_resumeCallback)}, + uuid{genUUID()}, + valid{_isValid} {} bool celix::GenericServiceDependency::isResolved() const { @@ -58,7 +66,7 @@ celix::Cardinality celix::GenericServiceDependency::getCardinality() const { return cardinality; } -bool celix::GenericServiceDependency::getRequired() const { +bool celix::GenericServiceDependency::isRequired() const { std::lock_guard<std::mutex> lck{mutex}; return required; } @@ -68,16 +76,42 @@ const std::string &celix::GenericServiceDependency::getFilter() const { return filter; } -std::string celix::GenericServiceDependency::getUUD() { +const std::string& celix::GenericServiceDependency::getUUD() const { return uuid; } -bool celix::GenericServiceDependency::isEnabled() { +bool celix::GenericServiceDependency::isEnabled() const { std::lock_guard<std::mutex> lck{mutex}; return !tracker.empty(); } -celix::ComponentState celix::GenericComponentManager::getState() const { +celix::UpdateServiceStrategy celix::GenericServiceDependency::getStrategy() const { + std::lock_guard<std::mutex> lck{mutex}; + return strategy; +} + +const std::string &celix::GenericServiceDependency::getSvcName() const { + return svcName; +} + +bool celix::GenericServiceDependency::isValid() const { + return valid; +} + +void celix::GenericServiceDependency::preServiceUpdate() { + if (getStrategy() == UpdateServiceStrategy::Suspense) { + suspenseCallback(); //note resume will be done in the updateServices (last called) + } +} + +void celix::GenericServiceDependency::postServiceUpdate() { + if (getStrategy() == UpdateServiceStrategy::Suspense) { + resumeCallback(); //note suspend call is done in the setService (first called) + } + this->stateChangedCallback(); +} + +celix::ComponentManagerState celix::GenericComponentManager:: getState() const { std::lock_guard<std::mutex> lck{stateMutex}; return state; } @@ -88,9 +122,12 @@ bool celix::GenericComponentManager::isEnabled() const { } bool celix::GenericComponentManager::isResolved() const { + if (!isEnabled()) { + return false; //not enabled is not resolved! + } std::lock_guard<std::mutex> lck{stateMutex}; for (auto &pair : serviceDependencies) { - if (!pair.second->isResolved()) { + if (pair.second->isEnabled() && !pair.second->isResolved()) { return false; } } @@ -98,32 +135,47 @@ bool celix::GenericComponentManager::isResolved() const { } void celix::GenericComponentManager::setEnabled(bool e) { - { - std::lock_guard<std::mutex> lck{stateMutex}; - enabled = e; - } std::vector<std::shared_ptr<GenericServiceDependency>> deps{}; + std::vector<std::shared_ptr<GenericProvidedService>> provides{}; { std::lock_guard<std::mutex> lck{serviceDependenciesMutex}; for (auto &pair : serviceDependencies) { deps.push_back(pair.second); } } - + { + std::lock_guard<std::mutex> lck{providedServicesMutex}; + for (auto &pair : providedServices) { + provides.push_back(pair.second); + } + } //NOTE updating outside of the lock - for (auto &dep : deps) { + for (auto& dep : deps) { dep->setEnabled(e); } + for (auto& provide : provides) { + provide->setEnabled(e); + } + + + { + std::lock_guard<std::mutex> lck{stateMutex}; + enabled = e; + } + updateState(); } void celix::GenericComponentManager::updateState() { - bool allDependenciesResolved = true; + bool allRequiredDependenciesResolved = true; /*all required and enabled dependencies resolved? */ { std::lock_guard<std::mutex> lck{serviceDependenciesMutex}; for (auto &pair : serviceDependencies) { - if (!pair.second->isResolved()) { - allDependenciesResolved = false; + auto enabled = pair.second->isEnabled(); + auto required = pair.second->isRequired(); + auto resolved = pair.second->isResolved(); + if (enabled && required && !resolved) { + allRequiredDependenciesResolved = false; break; } } @@ -131,13 +183,13 @@ void celix::GenericComponentManager::updateState() { { std::lock_guard<std::mutex> lck{stateMutex}; - ComponentState currentState = state; - ComponentState targetState = ComponentState::Disabled; + ComponentManagerState currentState = state; + ComponentManagerState targetState = ComponentManagerState::Disabled; - if (enabled && allDependenciesResolved) { - targetState = ComponentState::Started; - } else if (enabled) /*not all dep resolved*/ { - targetState = initialized ? ComponentState::Initialized : ComponentState::Uninitialized; + if (enabled && allRequiredDependenciesResolved) { + targetState = ComponentManagerState::ComponentStarted; + } else if (enabled) /*not all required dep resolved*/ { + targetState = initialized ? ComponentManagerState::ComponentInitialized : ComponentManagerState::ComponentUninitialized; } if (currentState != targetState) { @@ -148,8 +200,8 @@ void celix::GenericComponentManager::updateState() { } void celix::GenericComponentManager::transition() { - ComponentState currentState; - ComponentState targetState; + ComponentManagerState currentState; + ComponentManagerState targetState; { std::lock_guard<std::mutex> lck{stateMutex}; @@ -162,94 +214,69 @@ void celix::GenericComponentManager::transition() { targetState = pair.second; } - DLOG(INFO) << "Transition for " << name << " from " << currentState << " to " << targetState; - - //TODO note callbacks are called outside of mutex. make local copies or ensure that callback can only be changed with disabled component managers ... + DLOG(INFO) << "Transition " << *this << " from " << currentState << " to " << targetState; - if (targetState == ComponentState::Disabled) { - switch (currentState) { - case celix::ComponentState::Disabled: + if (currentState == ComponentManagerState::Disabled) { + switch (targetState) { + case celix::ComponentManagerState::Disabled: //nop break; - case celix::ComponentState::Uninitialized: - //TODO may disable callback to cmp - setState(ComponentState::Disabled); - break; - case celix::ComponentState::Initialized:; - deinit(); - setInitialized(false); - setState(ComponentState::Uninitialized); - break; - case celix::ComponentState::Started: - stop(); - setState(ComponentState::Initialized); + default: + setState(ComponentManagerState::ComponentUninitialized); break; } - } else if (targetState == ComponentState::Uninitialized) { - switch (currentState) { - case celix::ComponentState::Disabled: - //TODO maybe enable callback - setState(ComponentState::Uninitialized); + } else if (currentState == ComponentManagerState::ComponentUninitialized) { + switch (targetState) { + case celix::ComponentManagerState::Disabled: + setState(ComponentManagerState::Disabled); break; - case celix::ComponentState::Uninitialized: + case celix::ComponentManagerState::ComponentUninitialized: //nop break; - case celix::ComponentState::Initialized: - deinit(); - setInitialized(false); - setState(ComponentState::Uninitialized); - break; - case celix::ComponentState::Started: - stop(); - setState(ComponentState::Initialized); + default: //targetState initialized of started + initCmp(); + setInitialized(true); + setState(ComponentManagerState::ComponentInitialized); break; } - } else if (targetState == ComponentState::Initialized) { - switch (currentState) { - case celix::ComponentState::Disabled: - //TODO maybe enabled callback - setState(ComponentState::Uninitialized); + } else if (currentState == ComponentManagerState::ComponentInitialized) { + switch (targetState) { + case celix::ComponentManagerState::Disabled: + deinitCmp(); + setInitialized(false); + setState(ComponentManagerState::ComponentUninitialized); break; - case celix::ComponentState::Uninitialized: - init(); - setInitialized(true); - setState(ComponentState::Initialized); + case celix::ComponentManagerState::ComponentUninitialized: + deinitCmp(); + setInitialized(false); + setState(ComponentManagerState::ComponentUninitialized); break; - case celix::ComponentState::Initialized: + case celix::ComponentManagerState::ComponentInitialized: //nop break; - case celix::ComponentState::Started: - stop(); - setState(ComponentState ::Initialized); + case celix::ComponentManagerState::ComponentStarted: + startCmp(); + setState(ComponentManagerState ::ComponentStarted); + updateServiceRegistrations(); break; } - } else if (targetState == ComponentState::Started) { - switch (currentState) { - case celix::ComponentState::Disabled: - //TODO maybe enabled callback - setState(ComponentState::Uninitialized); - break; - case celix::ComponentState::Uninitialized: - init(); - setInitialized(true); - setState(ComponentState::Initialized); - break; - case celix::ComponentState::Initialized: - start(); - setState(ComponentState::Started); - break; - case celix::ComponentState::Started: + } else if (currentState == ComponentManagerState::ComponentStarted) { + switch (targetState) { + case celix::ComponentManagerState::ComponentStarted: //nop break; + default: + stopCmp(); + setState(ComponentManagerState::ComponentInitialized); + updateServiceRegistrations(); //TODO before stop? + break; } - } else { - LOG(ERROR) << "Unexpected target state: " << targetState; } updateState(); } -void celix::GenericComponentManager::setState(ComponentState s) { +void celix::GenericComponentManager::setState(ComponentManagerState s) { std::lock_guard<std::mutex> lck{stateMutex}; state = s; } @@ -263,13 +290,41 @@ celix::GenericComponentManager::GenericComponentManager(std::shared_ptr<celix::B } void celix::GenericComponentManager::removeServiceDependency(const std::string& serviceDependencyUUID) { - std::lock_guard<std::mutex> lck{stateMutex}; - auto it = serviceDependencies.find(serviceDependencyUUID); - if (it != serviceDependencies.end()) { - serviceDependencies.erase(it); - } else { - LOG(WARNING) << "Cannot find service dependency with uuid " << serviceDependencyUUID << " in component manager " << name << " with uuid " << uuid << "."; + std::shared_ptr<GenericServiceDependency> removed{}; + { + std::lock_guard<std::mutex> lck{serviceDependenciesMutex}; + auto it = serviceDependencies.find(serviceDependencyUUID); + if (it != serviceDependencies.end()) { + removed = it->second; + serviceDependencies.erase(it); + } else { + LOG(WARNING) << "Cannot find service dependency with uuid " << serviceDependencyUUID + << " in component manager " << name << " with uuid " << uuid << "."; + } + } + if (removed) { + removed->setEnabled(false); + } + //NOTE removed out of scope -> RAII will cleanup +} + +void celix::GenericComponentManager::removeProvideService(const std::string& provideServiceUUID) { + std::shared_ptr<GenericProvidedService> removed{}; + { + std::lock_guard<std::mutex> lck{providedServicesMutex}; + auto it = providedServices.find(provideServiceUUID); + if (it != providedServices.end()) { + removed = it->second; + providedServices.erase(it); + } else { + LOG(WARNING) << "Cannot find provided service with uuid " << provideServiceUUID + << " in component manager " << name << " with uuid " << uuid << "."; + } } + if (removed) { + removed->setEnabled(false); + } + //NOTE removed out of scope -> RAII will cleanup } std::string celix::GenericComponentManager::getName() const { @@ -281,23 +336,195 @@ void celix::GenericComponentManager::setInitialized(bool i) { initialized = i; } +std::shared_ptr<celix::GenericServiceDependency> +celix::GenericComponentManager::findGenericServiceDependency(const std::string &svcName, + const std::string &svcDepUUID) { + std::lock_guard<std::mutex> lck{serviceDependenciesMutex}; + auto it = serviceDependencies.find(svcDepUUID); + if (it != serviceDependencies.end()) { + if (it->second->getSvcName() == svcName) { + return it->second; + } else { + LOG(WARNING) << "Requested svc name has svc name " << it->second->getSvcName() << " instead of the requested " << svcName; + return nullptr; + } + } else { + LOG(WARNING) << "Cmp Manager (" << uuid << ") does not have a service dependency with uuid " << svcDepUUID << "; returning an invalid GenericServiceDependency"; + return nullptr; + } +} + +std::size_t celix::GenericComponentManager::getSuspendedCount() const { + std::lock_guard<std::mutex> lck{stateMutex}; + return suspendedCount; +} + +void celix::GenericComponentManager::suspense() { + bool changedSuspended = false; + { + std::lock_guard<std::mutex> lck{stateMutex}; + if (!suspended) { + suspended = true; + changedSuspended = true; + } + } + if (changedSuspended) { + transition(); + std::lock_guard<std::mutex> lck{stateMutex}; + suspendedCount += 1; + DLOG(INFO) << "Suspended " << *this; + } +} + +void celix::GenericComponentManager::resume() { + bool changedSuspended = false; + { + std::lock_guard<std::mutex> lck{stateMutex}; + if (suspended) { + suspended = false; + changedSuspended = true; + } + } + if (changedSuspended) { + transition(); + DLOG(INFO) << "Resumed " << *this; + } +} + +void celix::GenericComponentManager::updateServiceRegistrations() { + std::vector<std::shared_ptr<GenericProvidedService>> toRegisterServices{}; + std::vector<std::shared_ptr<GenericProvidedService>> toUnregisterServices{}; + if (getState() == ComponentManagerState::ComponentStarted) { + std::lock_guard<std::mutex> lck{providedServicesMutex}; + for (auto &pair : providedServices) { + auto enabled = pair.second->isEnabled(); + auto registered = pair.second->isServiceRegistered(); + if (enabled && !registered) { + toRegisterServices.push_back(pair.second); + } else if (registered && !enabled) { + toUnregisterServices.push_back(pair.second); + } + } + } else { + std::lock_guard<std::mutex> lck{providedServicesMutex}; + for (auto &pair : providedServices) { + auto registered = pair.second->isServiceRegistered(); + if (registered) { + toUnregisterServices.push_back(pair.second); + } + } + } + + //note (un)registering service outside of mutex + for (auto& provide : toRegisterServices) { + provide->registerService(); + } + for (auto& provide : toUnregisterServices) { + provide->unregisterService(); + } +} + +std::shared_ptr<celix::GenericProvidedService> +celix::GenericComponentManager::findGenericProvidedService(const std::string &svcName, + const std::string &providedServiceUUID) { + std::lock_guard<std::mutex> lck{providedServicesMutex}; + auto it = providedServices.find(providedServiceUUID); + if (it != providedServices.end()) { + if (it->second->getServiceName() == svcName) { + return it->second; + } else { + LOG(WARNING) << "Requested svc name has svc name " << it->second->getServiceName() << " instead of the requested " << svcName; + return nullptr; + } + } else { + LOG(WARNING) << "Cmp Manager (" << uuid << ") does not have a provided service with uuid " << providedServiceUUID << "; returning an invalid GenericProvidedService"; + return nullptr; + } +} + +std::size_t celix::GenericComponentManager::nrOfServiceDependencies() { + std::lock_guard<std::mutex> lck{serviceDependenciesMutex}; + return serviceDependencies.size(); +} + +std::size_t celix::GenericComponentManager::nrOfProvidedServices() { + std::lock_guard<std::mutex> lck{providedServicesMutex}; + return providedServices.size(); +} -std::ostream& operator<< (std::ostream& out, celix::ComponentState state) +std::ostream& operator<< (std::ostream& out, const celix::GenericComponentManager& mng) { + out << "ComponentManager[name=" << mng.getName() << ", uuid=" << mng.getUUD() << "]"; + return out; +} + + +std::ostream& operator<< (std::ostream& out, celix::ComponentManagerState state) { switch (state) { - case celix::ComponentState::Disabled: + case celix::ComponentManagerState::Disabled: out << "Disabled"; break; - case celix::ComponentState::Uninitialized: - out << "Uninitialized"; + case celix::ComponentManagerState::ComponentUninitialized: + out << "ComponentUninitialized"; break; - case celix::ComponentState::Initialized: - out << "Initialized"; + case celix::ComponentManagerState::ComponentInitialized: + out << "ComponentInitialized"; break; - case celix::ComponentState::Started: - out << "Started"; + case celix::ComponentManagerState::ComponentStarted: + out << "ComponentStarted"; break; } return out; } +bool celix::GenericProvidedService::isEnabled() const { + std::lock_guard<std::mutex> lck{mutex}; + return enabled; +} + +const std::string &celix::GenericProvidedService::getUUID() const { + return uuid; +} + +const std::string &celix::GenericProvidedService::getServiceName() const { + return svcName; +} + +bool celix::GenericProvidedService::isValid() const { + return valid; +} + +celix::GenericProvidedService::GenericProvidedService( + std::shared_ptr<celix::BundleContext> _ctx, + std::string _svcName, + std::function<void()> _updateServiceRegistrationsCallback, + bool _valid) : + ctx{std::move(_ctx)}, + uuid{genUUID()}, + svcName{std::move(_svcName)}, + updateServiceRegistrationsCallback{std::move(_updateServiceRegistrationsCallback)}, + valid{_valid} { + +} + +void celix::GenericProvidedService::setEnabled(bool e) { + { + std::lock_guard<std::mutex> lck{mutex}; + enabled = e; + } + updateServiceRegistrationsCallback(); +} + +void celix::GenericProvidedService::unregisterService() { + std::vector<ServiceRegistration> unregister{}; + { + std::lock_guard<std::mutex> lck{mutex}; + std::swap(registration, unregister); + } + //NOTE RAII will ensure unregister +} + +bool celix::GenericProvidedService::isServiceRegistered() { + std::lock_guard<std::mutex> lck{mutex}; + return registration.size() > 0; +} diff --git a/libs/registry/include/celix/Constants.h b/libs/registry/include/celix/Constants.h index 96f6326..d3d5cf8 100644 --- a/libs/registry/include/celix/Constants.h +++ b/libs/registry/include/celix/Constants.h @@ -24,15 +24,13 @@ namespace celix { //NOTE manually aligned with celix_constants.h - static constexpr const char *const SERVICE_NAME = "SERVICE_NAME"; - static constexpr const char *const SERVICE_ID = "SERVICE_ID"; - static constexpr const char *const SERVICE_RANKING = "SERVICE_RANKING"; - static constexpr const char *const SERVICE_BUNDLE = "SERVICE_BUNDLE"; + static constexpr const char *const SERVICE_NAME = "service.name"; + static constexpr const char *const SERVICE_ID = "service.id"; + static constexpr const char *const SERVICE_RANKING = "service.ranking"; + static constexpr const char *const SERVICE_BUNDLE = "service.bundle"; static constexpr const char *const FRAMEWORK_UUID = "framework.uuid"; - static constexpr const char *const C_AND_CXX_LANG_REG = "C/C++"; - static constexpr const char *const MANIFEST_BUNDLE_SYMBOLIC_NAME = "Bundle-SymbolicName"; static constexpr const char *const MANIFEST_BUNDLE_NAME = "Bundle-Name"; static constexpr const char *const MANIFEST_BUNDLE_VERSION = "Bundle-Version"; diff --git a/libs/registry/include/celix/ServiceRegistry.h b/libs/registry/include/celix/ServiceRegistry.h index 4457d0e..cc9e704 100644 --- a/libs/registry/include/celix/ServiceRegistry.h +++ b/libs/registry/include/celix/ServiceRegistry.h @@ -89,6 +89,13 @@ namespace celix { std::function<void(std::vector<std::tuple<std::shared_ptr<I>, const celix::Properties*, const celix::IResourceBundle *>> rankedServices)> updateWithOwner{}; //TODO lock free update calls atomics, rcu, hazard pointers ?? + + + /** + * pre and post update hooks, can be used if a trigger is needed before or after an service update. + */ + std::function<void()> preServiceUpdateHook{}; + std::function<void()> postServiceUpdateHook{}; }; //RAII service tracker: out of scope -> stop tracker @@ -130,11 +137,7 @@ namespace celix { const std::string& name() const; template<typename I> - celix::ServiceRegistration registerService(I &svc, celix::Properties props = {}, std::shared_ptr<const celix::IResourceBundle> owner = {}) { - auto svcName = celix::serviceName<I>(); - auto voidSvc = std::shared_ptr<void>(static_cast<void*>(&svc), [](void*){/*nop*/}); //transform to std::shared_ptr to minimize the underlining impl needed. - return registerService(svcName, std::move(voidSvc), std::move(props), std::move(owner)); - } + celix::ServiceRegistration registerService(I &&svc, celix::Properties props = {}, std::shared_ptr<const celix::IResourceBundle> owner = {}); template<typename I> celix::ServiceRegistration registerService(std::shared_ptr<I> svc, celix::Properties props = {}, std::shared_ptr<const celix::IResourceBundle> owner = {}) { @@ -400,6 +403,9 @@ namespace celix { } +std::ostream& operator<<(std::ostream &out, const celix::ServiceRegistration& serviceRegistration); + + template<typename F> inline celix::ServiceRegistration celix::ServiceRegistry::registerFunctionService(const std::string &functionName, F&& func, celix::Properties props, std::shared_ptr<const celix::IResourceBundle> owner) { class FunctionServiceFactory : public celix::IServiceFactory<void> { @@ -498,11 +504,11 @@ inline celix::ServiceTracker celix::ServiceRegistry::trackServices(std::string s ServiceTrackerOptions<void> opts{}; opts.filter = std::move(options.filter); - if (options.set != nullptr) { - auto set = std::move(options.set); - opts.set = [set](std::shared_ptr<void> svc){ + if (options.setWithOwner != nullptr) { + auto set = std::move(options.setWithOwner); + opts.setWithOwner = [set](std::shared_ptr<void> svc, const celix::Properties &props, const celix::IResourceBundle &owner){ auto typedSvc = std::static_pointer_cast<I>(svc); - set(typedSvc); + set(typedSvc, props, owner); }; } if (options.setWithProperties != nullptr) { @@ -512,19 +518,19 @@ inline celix::ServiceTracker celix::ServiceRegistry::trackServices(std::string s set(typedSvc, props); }; } - if (options.setWithOwner != nullptr) { - auto set = std::move(options.setWithOwner); - opts.setWithOwner = [set](std::shared_ptr<void> svc, const celix::Properties &props, const celix::IResourceBundle &owner){ + if (options.set != nullptr) { + auto set = std::move(options.set); + opts.set = [set](std::shared_ptr<void> svc){ auto typedSvc = std::static_pointer_cast<I>(svc); - set(typedSvc, props, owner); + set(typedSvc); }; } - if (options.add != nullptr) { - auto add = std::move(options.add); - opts.add = [add](std::shared_ptr<void> svc) { + if (options.addWithOwner != nullptr) { + auto add = std::move(options.addWithOwner); + opts.addWithOwner = [add](std::shared_ptr<void> svc, const celix::Properties &props, const celix::IResourceBundle &bnd) { auto typedSvc = std::static_pointer_cast<I>(svc); - add(typedSvc); + add(typedSvc, props, bnd); }; } if (options.addWithProperties != nullptr) { @@ -534,19 +540,19 @@ inline celix::ServiceTracker celix::ServiceRegistry::trackServices(std::string s add(typedSvc, props); }; } - if (options.addWithOwner != nullptr) { - auto add = std::move(options.addWithOwner); - opts.addWithOwner = [add](std::shared_ptr<void> svc, const celix::Properties &props, const celix::IResourceBundle &bnd) { + if (options.add != nullptr) { + auto add = std::move(options.add); + opts.add = [add](std::shared_ptr<void> svc) { auto typedSvc = std::static_pointer_cast<I>(svc); - add(typedSvc, props, bnd); + add(typedSvc); }; } - if (options.remove != nullptr) { - auto rem = std::move(options.remove); - opts.remove = [rem](std::shared_ptr<void> svc) { + if (options.removeWithOwner != nullptr) { + auto rem = std::move(options.removeWithOwner); + opts.removeWithOwner = [rem](std::shared_ptr<void> svc, const celix::Properties &props, const celix::IResourceBundle &bnd) { auto typedSvc = std::static_pointer_cast<I>(svc); - rem(typedSvc); + rem(typedSvc, props, bnd); }; } if (options.removeWithProperties != nullptr) { @@ -556,22 +562,22 @@ inline celix::ServiceTracker celix::ServiceRegistry::trackServices(std::string s rem(typedSvc, props); }; } - if (options.removeWithOwner != nullptr) { - auto rem = std::move(options.removeWithOwner); - opts.removeWithOwner = [rem](std::shared_ptr<void> svc, const celix::Properties &props, const celix::IResourceBundle &bnd) { + if (options.remove != nullptr) { + auto rem = std::move(options.remove); + opts.remove = [rem](std::shared_ptr<void> svc) { auto typedSvc = std::static_pointer_cast<I>(svc); - rem(typedSvc, props, bnd); + rem(typedSvc); }; } - if (options.update != nullptr) { - auto update = std::move(options.update); - opts.update = [update](std::vector<std::shared_ptr<void>> rankedServices) { - std::vector<std::shared_ptr<I>> typedServices{}; + if (options.updateWithOwner != nullptr) { + auto update = std::move(options.updateWithOwner); + opts.updateWithOwner = [update](std::vector<std::tuple<std::shared_ptr<void>, const celix::Properties *, const celix::IResourceBundle*>> rankedServices) { + std::vector<std::tuple<std::shared_ptr<I>, const celix::Properties*, const celix::IResourceBundle*>> typedServices{}; typedServices.reserve(rankedServices.size()); - for (auto &svc : rankedServices) { - auto typedSvc = std::static_pointer_cast<I>(svc); - typedServices.push_back(typedSvc); + for (auto &tuple : rankedServices) { + auto typedSvc = std::static_pointer_cast<I>(std::get<0>(tuple)); + typedServices.push_back(std::make_tuple(typedSvc, std::get<1>(tuple), std::get<2>(tuple))); } update(std::move(typedServices)); }; @@ -588,19 +594,26 @@ inline celix::ServiceTracker celix::ServiceRegistry::trackServices(std::string s update(std::move(typedServices)); }; } - if (options.updateWithOwner != nullptr) { - auto update = std::move(options.updateWithOwner); - opts.updateWithOwner = [update](std::vector<std::tuple<std::shared_ptr<void>, const celix::Properties *, const celix::IResourceBundle*>> rankedServices) { - std::vector<std::tuple<std::shared_ptr<I>, const celix::Properties*, const celix::IResourceBundle*>> typedServices{}; + if (options.update != nullptr) { + auto update = std::move(options.update); + opts.update = [update](std::vector<std::shared_ptr<void>> rankedServices) { + std::vector<std::shared_ptr<I>> typedServices{}; typedServices.reserve(rankedServices.size()); - for (auto &tuple : rankedServices) { - auto typedSvc = std::static_pointer_cast<I>(std::get<0>(tuple)); - typedServices.push_back(std::make_tuple(typedSvc, std::get<1>(tuple), std::get<2>(tuple))); + for (auto &svc : rankedServices) { + auto typedSvc = std::static_pointer_cast<I>(svc); + typedServices.push_back(typedSvc); } update(std::move(typedServices)); }; } + if (options.preServiceUpdateHook) { + opts.preServiceUpdateHook = std::move(options.preServiceUpdateHook); + } + if (options.postServiceUpdateHook) { + opts.postServiceUpdateHook = std::move(options.postServiceUpdateHook); + } + return trackAnyServices(std::move(svcName), std::move(opts), std::move(requester)); } diff --git a/libs/registry/src/ServiceRegistry.cc b/libs/registry/src/ServiceRegistry.cc index a8cf2f5..c6e41a9 100644 --- a/libs/registry/src/ServiceRegistry.cc +++ b/libs/registry/src/ServiceRegistry.cc @@ -28,6 +28,8 @@ #include <glog/logging.h> #include <assert.h> +#include <celix/ServiceRegistry.h> + #include "celix/Constants.h" #include "celix/ServiceRegistry.h" @@ -196,9 +198,15 @@ namespace { } //call callbacks + if (opts.preServiceUpdateHook) { + opts.preServiceUpdateHook(); + } callSetCallbacks(); callAddRemoveCallbacks(entry, svc, true); callUpdateCallbacks(); + if (opts.postServiceUpdateHook) { + opts.postServiceUpdateHook(); + } } void remMatch(const std::shared_ptr<const SvcEntry> &entry) { @@ -211,26 +219,32 @@ namespace { } //call callbacks + if (opts.preServiceUpdateHook) { + opts.preServiceUpdateHook(); + } callSetCallbacks(); //note also removed highest if that was set to this svc callAddRemoveCallbacks(entry, svc, false); callUpdateCallbacks(); - + if (opts.postServiceUpdateHook) { + opts.postServiceUpdateHook(); + } //note sync will be done on the SvcEntry usage, which is controlled by the tracker svc shared ptr } - void callAddRemoveCallbacks(const std::shared_ptr<const SvcEntry> &entry, std::shared_ptr<void> &svc, bool add) { - auto &update = add ? opts.add : opts.remove; - auto &updateWithProps = add ? opts.addWithProperties : opts.removeWithProperties; - auto &updateWithOwner = add ? opts.addWithOwner : opts.removeWithOwner; - if (update) { - update(svc); + void callAddRemoveCallbacks(const std::shared_ptr<const SvcEntry>& entry, std::shared_ptr<void>& svc, bool add) { + auto& update = add ? opts.add : opts.remove; + auto& updateWithProps = add ? opts.addWithProperties : opts.removeWithProperties; + auto& updateWithOwner = add ? opts.addWithOwner : opts.removeWithOwner; + //The more explicit callbacks are called first (i.e first WithOwner) + if (updateWithOwner) { + updateWithOwner(svc, entry->props, *entry->owner); } if (updateWithProps) { updateWithProps(svc, entry->props); } - if (updateWithOwner) { - updateWithOwner(svc, entry->props, *entry->owner); + if (update) { + update(svc); } } @@ -254,14 +268,19 @@ namespace { //TODO race condition. highest can be updated because lock is released. if (highestUpdated) { - if (opts.set) { - opts.set(currentHighestSvc); //note can be nullptr + static celix::Properties emptyProps{}; + static EmptyBundle emptyOwner{}; + //The more explicit callbacks are called first (i.e first WithOwner) + if (opts.setWithOwner) { + opts.setWithOwner(currentHighestSvc, + currentHighestSvc == nullptr ? emptyProps : currentHighestSvcEntry->props, + currentHighestSvc == nullptr ? emptyOwner : *currentHighestSvcEntry->owner); } if (opts.setWithProperties) { - opts.setWithProperties(currentHighestSvc, currentHighestSvcEntry->props); + opts.setWithProperties(currentHighestSvc, currentHighestSvc == nullptr ? emptyProps : currentHighestSvcEntry->props); } - if (opts.setWithOwner) { - opts.setWithOwner(currentHighestSvc, currentHighestSvcEntry->props, *currentHighestSvcEntry->owner); + if (opts.set) { + opts.set(currentHighestSvc); //note can be nullptr } } } @@ -276,12 +295,9 @@ namespace { rankedServices.push_back(std::make_tuple(entry.second, &entry.first->props, entry.first->owner.get())); } } - if (opts.update) { - std::vector<std::shared_ptr<void>> rnk{}; - for (auto &tuple : rankedServices) { - rnk.push_back(std::get<0>(tuple)); - } - opts.update(std::move(rnk)); + //The more explicit callbacks are called first (i.e first WithOwner) + if (opts.updateWithOwner) { + opts.updateWithOwner(rankedServices); } if (opts.updateWithProperties) { std::vector<std::tuple<std::shared_ptr<void>, const celix::Properties*>> rnk{}; @@ -290,8 +306,12 @@ namespace { } opts.updateWithProperties(std::move(rnk)); } - if (opts.updateWithOwner) { - opts.updateWithOwner(std::move(rankedServices)); + if (opts.update) { + std::vector<std::shared_ptr<void>> rnk{}; + for (auto &tuple : rankedServices) { + rnk.push_back(std::get<0>(tuple)); + } + opts.update(std::move(rnk)); } } @@ -353,7 +373,7 @@ public: class celix::ServiceRegistry::Impl { public: - Impl(std::string _regName) : regName{_regName} {} + Impl(std::string _regName) : regName{std::move(_regName)} {} const std::shared_ptr<const celix::IResourceBundle> emptyBundle = std::shared_ptr<const celix::IResourceBundle>{new EmptyBundle{}}; const std::string regName; @@ -403,9 +423,9 @@ public: props[celix::SERVICE_BUNDLE] = std::to_string(bnd->id()); if (factory) { - VLOG(1) << "Registering service factory '" << svcName << "' from bundle id " << owner->id() << std::endl; + DLOG(INFO) << "Registering service factory '" << svcName << "' from bundle id " << owner->id() << std::endl; } else { - VLOG(1) << "Registering service '" << svcName << "' from bundle id " << owner->id() << std::endl; + DLOG(INFO) << "Registering service '" << svcName << "' from bundle id " << owner->id() << std::endl; } const auto it = services.registry[svcName].emplace(new SvcEntry{std::move(bnd), svcId, svcName, std::move(svc), std::move(factory), std::move(props)}); @@ -430,6 +450,7 @@ public: .unregisterCallback = std::move(unreg), .registered = true }; + return celix::ServiceRegistration{impl}; } @@ -524,7 +545,7 @@ public: /********************************************************************************************************************** - Service Registry + Service Registry Implementation **********************************************************************************************************************/ @@ -539,11 +560,6 @@ celix::ServiceRegistry::~ServiceRegistry() { const std::string& celix::ServiceRegistry::name() const { return pimpl->regName; } -/* -celix::ServiceRegistration celix::ServiceRegistry::registerService(const std::string &svcName, std::unique_ptr<void> svc, celix::Properties props, std::shared_ptr<const celix::IResourceBundle> owner) { - return pimpl->registerService(std::move(svcName), nullptr, {}, std::move(svc), std::move(props), std::move(owner)); -}*/ - celix::ServiceRegistration celix::ServiceRegistry::registerService(std::string svcName, std::shared_ptr<void> svc, celix::Properties props, std::shared_ptr<const celix::IResourceBundle> owner) { return pimpl->registerService(std::move(svcName), std::move(svc), {}, std::move(props), std::move(owner)); } @@ -728,8 +744,17 @@ std::vector<std::string> celix::ServiceRegistry::listAllRegisteredServiceNames() return pimpl->listAllRegisteredServiceNames(); } +template<typename I> +celix::ServiceRegistration celix::ServiceRegistry::registerService(I &&svc, celix::Properties props, std::shared_ptr<const celix::IResourceBundle> owner) { + auto svcName = celix::serviceName<I>(); + auto *servicesStore = new std::vector<I>{}; + servicesStore->emplace_back(std::forward<I>(svc)); + auto voidSvc = std::shared_ptr<void>(static_cast<void*>(&(*servicesStore)[0]), [servicesStore](void*){delete servicesStore;}); //transform to std::shared_ptr to minimize the underlining impl needed. + return registerService(svcName, std::move(voidSvc), std::move(props), std::move(owner)); +} + /********************************************************************************************************************** - Service Registration + Service Registration Implementation **********************************************************************************************************************/ celix::ServiceRegistration::ServiceRegistration() : pimpl{nullptr} {} @@ -748,6 +773,7 @@ void celix::ServiceRegistration::unregister() { if (pimpl && pimpl->registered) { pimpl->registered = false; //TODO make thread safe pimpl->unregisterCallback(); + DLOG(INFO) << "Unregister " << *this; } } @@ -764,11 +790,16 @@ const std::string& celix::ServiceRegistration::serviceName() const { return empty; } +std::ostream& operator<<(std::ostream &out, const celix::ServiceRegistration& reg) { + out << "ServiceRegistration[service_id=" << reg.serviceId() << ",service_name=" << reg.serviceName() << "]"; + return out; +} + /********************************************************************************************************************** - Service Tracker + Service Tracker Implementation **********************************************************************************************************************/ celix::ServiceTracker::ServiceTracker() : pimpl{nullptr} {}