This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/87-add-additional-svc-tracker-bundle-context-funtions in repository https://gitbox.apache.org/repos/asf/celix.git
The following commit(s) were added to refs/heads/feature/87-add-additional-svc-tracker-bundle-context-funtions by this push: new 9321f514 gh-87: Add useService(s) methods to C++ ServiceTracker 9321f514 is described below commit 9321f514d4613b577debab7f3e8e9fc0a46410d0 Author: Pepijn Noltes <pnol...@apache.org> AuthorDate: Sun Feb 11 17:17:34 2024 +0100 gh-87: Add useService(s) methods to C++ ServiceTracker --- .../gtest/src/CxxBundleContextTestSuite.cc | 103 ++++++++++++++++++ libs/framework/include/celix/Trackers.h | 119 +++++++++++++++++++++ 2 files changed, 222 insertions(+) diff --git a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc index 5155c2bb..b987d28b 100644 --- a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc +++ b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc @@ -784,3 +784,106 @@ TEST_F(CxxBundleContextTestSuite, TestOldCStyleTrackerWithCxxMetaTracker) { serviceTracker_close(tracker); serviceTracker_destroy(tracker); } + +TEST_F(CxxBundleContextTestSuite, UseTrackedServidesTest) { + // Given 2 registered services + auto svc1 = std::make_shared<CInterface>(CInterface{nullptr, nullptr}); + auto svcReg1 = ctx->registerService<CInterface>(svc1).build(); + auto svc2 = std::make_shared<CInterface>(CInterface{nullptr, nullptr}); + auto svcReg2 = ctx->registerService<CInterface>(svc2).build(); + + // And a tracker for the services + auto tracker = ctx->trackServices<CInterface>().build(); + tracker->wait(); + + // Then I can use the useServices method to use the services + int count{0}; // note useService(s) callback are called in the current thread, so no need for atomic + size_t nrCalled = tracker->useServices([&count](CInterface& svc) { + (void)svc; + count++; + }); + EXPECT_EQ(2, nrCalled); + EXPECT_EQ(2, count); + + // And I can use the useServicesWithProperties method to use the services with their properties + count = 0; + nrCalled = tracker->useServicesWithProperties([&count](CInterface& svc, const celix::Properties& props) { + (void)svc; + long svcId = props.getAsLong(CELIX_FRAMEWORK_SERVICE_ID, -1L); + EXPECT_GE(svcId, 0); + count++; + }); + EXPECT_EQ(2, nrCalled); + EXPECT_EQ(2, count); + + // And I can use the useServicesWithOwner method to use the services with their properties and the bundle + count = 0; + nrCalled = tracker->useServicesWithOwner( + [&count](CInterface& svc, const celix::Properties& props, const celix::Bundle& bnd) { + (void)svc; + long svcId = props.getAsLong(CELIX_FRAMEWORK_SERVICE_ID, -1L); + EXPECT_GE(svcId, 0); + EXPECT_GE(bnd.getId(), 0); + count++; + }); + EXPECT_EQ(2, nrCalled); + EXPECT_EQ(2, count); + + // And I can use the useService method to use the highest ranking service + count = 0; + bool called = tracker->useService([&count](CInterface& svc) { + (void)svc; + count++; + }); + EXPECT_TRUE(called); + EXPECT_EQ(1, count); + + // And I can use the useServiceWithProperties method to use the highest ranking service with its properties + count = 0; + called = tracker->useServiceWithProperties([&count](CInterface& svc, const celix::Properties& props) { + (void)svc; + long svcId = props.getAsLong(CELIX_FRAMEWORK_SERVICE_ID, -1L); + EXPECT_GE(svcId, 0); + count++; + }); + EXPECT_TRUE(called); + EXPECT_EQ(1, count); + + // And I can use the useServiceWithOwner method to use the highest ranking service with its properties and the + // bundle + count = 0; + called = tracker->useServiceWithOwner( + [&count](CInterface& svc, const celix::Properties& props, const celix::Bundle& bnd) { + (void)svc; + long svcId = props.getAsLong(CELIX_FRAMEWORK_SERVICE_ID, -1L); + EXPECT_GE(svcId, 0); + EXPECT_GE(bnd.getId(), 0); + count++; + }); + EXPECT_TRUE(called); + EXPECT_EQ(1, count); + + // When registering a new service with a service raking of 100 + auto svc3 = std::make_shared<CInterface>(CInterface{nullptr, nullptr}); + auto svcReg3 = ctx->registerService<CInterface>(svc3).addProperty(celix::SERVICE_RANKING, 100).build(); + svcReg3->wait(); + + // Then the useServices method returns 3 services + count = 0; + nrCalled = tracker->useServices([&count](CInterface& svc) { + (void)svc; + count++; + }); + EXPECT_EQ(3, nrCalled); + EXPECT_EQ(3, count); + + // And the useServiceWithProperties method is called with a service with a service ranking of 100 and returns true + count = 0; + called = tracker->useServiceWithProperties([&count](CInterface& svc, const celix::Properties& props) { + (void)svc; + long ranking = props.getAsLong(celix::SERVICE_RANKING, -1L); + EXPECT_EQ(100, ranking); + count++; + }); + EXPECT_TRUE(called); +} diff --git a/libs/framework/include/celix/Trackers.h b/libs/framework/include/celix/Trackers.h index 60df7c86..1521757e 100644 --- a/libs/framework/include/celix/Trackers.h +++ b/libs/framework/include/celix/Trackers.h @@ -371,6 +371,98 @@ namespace celix { } return result; } + + /** + * @brief Applies the provided function to each service being tracked. + * + * @tparam F A function or callable object type. The function signature should be equivalent to the following: + * `void func(I& svc)` + * where I is the service type being tracked. + * @param f The function or callable object to apply to each service. + * @return The number of services to which the function was applied. + */ + template<typename F> + size_t useServices(const F& f) { + return this->template useServicesInternal( + [&f](I& svc, const celix::Properties&, const celix::Bundle&) { f(svc); }); + } + + /** + * @brief Applies the provided function to each service being tracked, along with its properties. + * + * @tparam F A function or callable object type. The function signature should be equivalent to the following: + * `void func(I& svc, const celix::Properties& props)` + * where I is the service type being tracked. + * @param f The function or callable object to apply to each service. + * @return The number of services to which the function was applied. + */ + template<typename F> + size_t useServicesWithProperties(const F& f) { + return this->template useServicesInternal( + [&f](I& svc, const celix::Properties& props, const celix::Bundle&) { f(svc, props); }); + } + + /** + * @brief Applies the provided function to each service being tracked, along with its properties and owner + * bundle. + * + * @tparam F A function or callable object type. The function signature should be equivalent to the following: + * `void func(I& svc, const celix::Properties& props, const celix::Bundle& bnd)` + * where I is the service type being tracked. + * @param f The function or callable object to apply to each service. + * @return The number of services to which the function was applied. + */ + template<typename F> + size_t useServicesWithOwner(const F& f) { + return this->template useServicesInternal( + [&f](I& svc, const celix::Properties& props, const celix::Bundle& bnd) { f(svc, props, bnd); }); + } + + /** + * @brief Applies the provided function to the highest ranking service being tracked. + * + * @tparam F A function or callable object type. The function signature should be equivalent to the following: + * `void func(I& svc)` + * where I is the service type being tracked. + * @param f The function or callable object to apply to the highest ranking service. + * @return True if the function was applied to a service, false otherwise. + */ + template<typename F> + bool useService(const F& f) { + return this->template useServiceInternal( + [&f](I& svc, const celix::Properties&, const celix::Bundle&) { f(svc); }); + } + + /** + * @brief Applies the provided function to the highest ranking service being tracked, along with its properties. + * + * @tparam F A function or callable object type. The function signature should be equivalent to the following: + * `void func(I& svc, const celix::Properties& props)` + * where I is the service type being tracked. + * @param f The function or callable object to apply to the highest ranking service. + * @return True if the function was applied to a service, false otherwise. + */ + template<typename F> + bool useServiceWithProperties(const F& f) { + return this->template useServiceInternal( + [&f](I& svc, const celix::Properties& props, const celix::Bundle&) { f(svc, props); }); + } + + /** + * @brief Applies the provided function to the highest ranking service being tracked, along with its properties + * and owner bundle. + * + * @tparam F A function or callable object type. The function signature should be equivalent to the following: + * `void func(I& svc, const celix::Properties& props, const celix::Bundle& bnd)` + * where I is the service type being tracked. + * @param f The function or callable object to apply to the highest ranking service. + * @return True if the function was applied to a service, false otherwise. + */ + template<typename F> + bool useServiceWithOwner(const F& f) { + return this->template useServiceInternal( + [&f](I& svc, const celix::Properties& props, const celix::Bundle& bnd) { f(svc, props, bnd); }); + } protected: struct SvcEntry { SvcEntry(long _svcId, long _svcRanking, std::shared_ptr<I> _svc, @@ -561,6 +653,33 @@ namespace celix { tracker->waitForExpiredSvcEntry(prevEntry); }; } + + template<typename F> + size_t useServicesInternal(const F& f) { + size_t count = 0; + std::lock_guard<std::mutex> lck{mutex}; + for (auto& e : entries) { + I& svc = *e->svc; + const celix::Properties& props = *e->properties; + const celix::Bundle& owner = *e->owner; + f(svc, props, owner); + count++; + } + return count; + } + + template<typename F> + bool useServiceInternal(const F& f) { + std::lock_guard<std::mutex> lck{mutex}; + if (highestRankingServiceEntry) { + I& svc = *highestRankingServiceEntry->svc; + const celix::Properties& props = *highestRankingServiceEntry->properties; + const celix::Bundle& owner = *highestRankingServiceEntry->owner; + f(svc, props, owner); + return true; + } + return false; + } }; /**