Repository: celix Updated Branches: refs/heads/feature/CELIX-426-cxx-api 7df4d26d9 -> e3339ca42
CELIX-426: Fixes some tests and improves C++ services example Project: http://git-wip-us.apache.org/repos/asf/celix/repo Commit: http://git-wip-us.apache.org/repos/asf/celix/commit/e3339ca4 Tree: http://git-wip-us.apache.org/repos/asf/celix/tree/e3339ca4 Diff: http://git-wip-us.apache.org/repos/asf/celix/diff/e3339ca4 Branch: refs/heads/feature/CELIX-426-cxx-api Commit: e3339ca42d586364e4970ed8cfe53649c5553241 Parents: 7df4d26 Author: Pepijn Noltes <[email protected]> Authored: Wed May 9 18:06:46 2018 +0200 Committer: Pepijn Noltes <[email protected]> Committed: Wed May 9 18:06:46 2018 +0200 ---------------------------------------------------------------------- .../src/ConsumerBundleActivator.cc | 39 ++++++---- .../src/ProviderBundleActivator.cc | 38 ++++------ framework/gtest/src/cxx_BundleContext_tests.cc | 79 ++++++++++++-------- framework/include/celix/BundleContext.h | 53 +++++++++++-- 4 files changed, 130 insertions(+), 79 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/celix/blob/e3339ca4/examples/celix-examples/services_example_cxx/src/ConsumerBundleActivator.cc ---------------------------------------------------------------------- diff --git a/examples/celix-examples/services_example_cxx/src/ConsumerBundleActivator.cc b/examples/celix-examples/services_example_cxx/src/ConsumerBundleActivator.cc index 5ad00fe..3371cad 100644 --- a/examples/celix-examples/services_example_cxx/src/ConsumerBundleActivator.cc +++ b/examples/celix-examples/services_example_cxx/src/ConsumerBundleActivator.cc @@ -19,6 +19,7 @@ #include <iostream> #include <thread> +#include <atomic> #include "celix/BundleActivator.h" @@ -27,15 +28,21 @@ namespace { class BundleActivator : public celix::IBundleActivator { public: - BundleActivator(celix::BundleContext &_ctx) : ctx{_ctx} {} + BundleActivator(celix::BundleContext &_ctx) : ctx{_ctx} { + this->trackerId = ctx.trackServices<example::ICalc>(example::ICalc::NAME, + [this](example::ICalc *, const celix::Properties &, const celix::Bundle&) { this->trackCount += 1; }, + [this](example::ICalc *, const celix::Properties &, const celix::Bundle&) { this->trackCount -= 1; }); + } virtual ~BundleActivator() { - this->useThread.join(); + ctx.stopTracker(this->trackerId); + this->running = false; + this->useThread.join(); } protected: void use() { - while(this->running) { + while(running) { int count = 0; double total = 0; ctx.useServices<example::ICalc>(example::ICalc::NAME, [&](example::ICalc &calc, const celix::Properties &, const celix::Bundle&) { @@ -43,26 +50,28 @@ namespace { total += calc.calc(1); }); std::cout << "Called calc " << count << " times. Total is " << total << std::endl; - std::this_thread::sleep_for(std::chrono::seconds(5)); - } - } - void setRunning(bool r) { - std::lock_guard<std::mutex> lock{this->mutex}; - this->running = r; - } + ctx.useService<example::ICalc>(example::ICalc::NAME, [&](example::ICalc &, const celix::Properties &props, const celix::Bundle &bnd){ + long rank = celix::getProperty(props, celix::Constants::SERVICE_RANKING, -1L); + long svcId = celix::getProperty(props, celix::Constants::SERVICE_ID, -1L); + long bndId = bnd.getBundleId(); + std::cout << "Found highest ranking call with rank " << rank << " and service id " << svcId << " from bundle " << bndId << std::endl; + }); + + std::cout << "track counter is " << this->trackCount << std::endl; - bool isRunning() { - std::lock_guard<std::mutex> lock{this->mutex}; - return this->running; + std::this_thread::sleep_for(std::chrono::seconds(5)); + } } private: celix::BundleContext &ctx; + + long trackerId{-1}; std::thread useThread{[this] { this->use(); }}; - std::mutex mutex{}; //protects running - bool running{true}; + std::atomic<bool> running{true}; + std::atomic<int> trackCount{0}; }; } http://git-wip-us.apache.org/repos/asf/celix/blob/e3339ca4/examples/celix-examples/services_example_cxx/src/ProviderBundleActivator.cc ---------------------------------------------------------------------- diff --git a/examples/celix-examples/services_example_cxx/src/ProviderBundleActivator.cc b/examples/celix-examples/services_example_cxx/src/ProviderBundleActivator.cc index fd8466e..ca8cf8d 100644 --- a/examples/celix-examples/services_example_cxx/src/ProviderBundleActivator.cc +++ b/examples/celix-examples/services_example_cxx/src/ProviderBundleActivator.cc @@ -19,7 +19,7 @@ #include <iostream> #include <thread> -#include <mutex> +#include <atomic> #include <vector> #include "celix/BundleActivator.h" @@ -44,25 +44,23 @@ namespace { CalcImpl calc{}; std::vector<long> svcIds{}; bool up = true; - while (this->isRunning()) { + while (this->running) { if (up) { celix::Properties props{}; - props[celix::Constants::SERVICE_RANKING] = "10"; //TODO random + props[celix::Constants::SERVICE_RANKING] = std::to_string(std::rand()); long svcId = ctx.registerService(example::ICalc::NAME, &calc, example::ICalc::VERSION, std::move(props)); svcIds.push_back(svcId); } else { - if (svcIds.size() > 0) { - long svcId = svcIds.back(); - svcIds.pop_back(); - ctx.unregisterService(svcId); - } else { - up = true; - } + long svcId = svcIds.back(); + svcIds.pop_back(); + ctx.unregisterService(svcId); } - if (svcIds.size() >= 100) { - up = false; + if (up) { + up = svcIds.size() < 100; + } else { + up = svcIds.size() == 0; } - std::this_thread::yield(); + std::this_thread::sleep_for(std::chrono::milliseconds(88)); } std::cout << "Exiting service register thread, services count is " << svcIds.size() << std::endl; std::for_each(svcIds.begin(), svcIds.end(), [&ctx](long id){ctx.unregisterService(id);}); @@ -71,24 +69,14 @@ namespace { } virtual ~BundleActivator() { - this->setRunning(false); + this->running = false; th.join(); } - void setRunning(bool r) { - std::lock_guard<std::mutex> lock{this->mutex}; - this->running = r; - } - - bool isRunning() { - std::lock_guard<std::mutex> lock{this->mutex}; - return this->running; - } private: std::thread th{}; - std::mutex mutex{}; //protects running - bool running{true}; + std::atomic<bool> running{true}; }; } http://git-wip-us.apache.org/repos/asf/celix/blob/e3339ca4/framework/gtest/src/cxx_BundleContext_tests.cc ---------------------------------------------------------------------- diff --git a/framework/gtest/src/cxx_BundleContext_tests.cc b/framework/gtest/src/cxx_BundleContext_tests.cc index 7687011..a426d66 100644 --- a/framework/gtest/src/cxx_BundleContext_tests.cc +++ b/framework/gtest/src/cxx_BundleContext_tests.cc @@ -37,6 +37,23 @@ private: std::unique_ptr<celix::Framework> fw_ptr{nullptr}; }; +//Test interface +class ITestSvc { +public: + static constexpr const char * const NAME = "ITestSvc"; + + virtual ~ITestSvc(){}; + virtual int calc(int input) = 0; +}; + +//Test implementation +class TestImpl : public ITestSvc { +public: + virtual ~TestImpl(){}; + int calc(int input) override { return input * 42; } +}; + + TEST_F(BundleContextTest, TestInstallBundle) { auto &ctx = this->framework().getFrameworkContext(); @@ -70,36 +87,50 @@ TEST_F(BundleContextTest, RegisterCServiceTest) { EXPECT_TRUE(svcId2 > 0); EXPECT_NE(svcId, svcId2); //new registration new id ctx.unregisterService(svcId2); + + //NOTE compile error -> cxxSvc is not POD + //TestImpl cxxSvc{}; + //ctx.registerCService(ITestSvc::NAME, &cxxSvc); } -TEST_F(BundleContextTest, UseService) { - struct test_svc { - int (*calc)(int input); - }; +TEST_F(BundleContextTest, RegisterServiceTest) { + auto &ctx = this->framework().getFrameworkContext(); + + TestImpl svc1{}; + long svcId = ctx.registerService<ITestSvc>(ITestSvc::NAME, &svc1); + EXPECT_TRUE(svcId > 0); + ctx.unregisterService(svcId); + + long svcId2 = ctx.registerService<ITestSvc>(ITestSvc::NAME, &svc1); + EXPECT_TRUE(svcId2 > 0); + EXPECT_NE(svcId, svcId2); //new registration new id + ctx.unregisterService(svcId2); +} + +TEST_F(BundleContextTest, UseService) { auto &ctx = this->framework().getFrameworkContext(); - test_svc svc1{}; - svc1.calc = [](int input) -> int { - return input * 42; - }; + TestImpl svc1{}; - long svcId = ctx.registerCService("test service", &svc1); + long svcId = ctx.registerService<ITestSvc>(ITestSvc::NAME, &svc1); EXPECT_TRUE(svcId > 0); int result = -1; - std::function<void(test_svc &svc, const celix::Properties&, const celix::Bundle&)> func = [&result](test_svc &svc, const celix::Properties&, const celix::Bundle&) { + std::function<void(ITestSvc &svc, const celix::Properties&, const celix::Bundle&)> func = [&result](ITestSvc &svc, const celix::Properties&, const celix::Bundle&) { result = svc.calc(1); }; - ctx.useService<test_svc>(svcId, "test service", func); + bool called = ctx.useService<ITestSvc>(ITestSvc::NAME, func); + EXPECT_TRUE(called); EXPECT_EQ(result, 42); - result = -1; - ctx.useService<test_svc>(svcId, "test service", [&result](test_svc &svc, const celix::Properties&, const celix::Bundle&) { - result = svc.calc(2); - }); - EXPECT_EQ(result, 84); +// result = -1; +// called = ctx.useServiceWithId<ITestSvc>(svcId, ITestSvc::NAME, [&result](ITestSvc &svc, const celix::Properties&, const celix::Bundle&) { +// result = svc.calc(2); +// }); +// EXPECT_TRUE(called); +// EXPECT_EQ(result, 84); ctx.unregisterService(svcId); } @@ -107,16 +138,6 @@ TEST_F(BundleContextTest, UseService) { TEST_F(BundleContextTest, UseServices) { auto &ctx = this->framework().getFrameworkContext(); - class ITestSvc { - public: - virtual ~ITestSvc(){}; - virtual int calc(int input) = 0; - }; - class TestImpl : public ITestSvc { - public: - virtual ~TestImpl(){}; - int calc(int input) override { return input * 42; } - }; TestImpl svc{}; long svcId1 = ctx.registerService<ITestSvc>("test service", &svc); @@ -147,12 +168,6 @@ TEST_F(BundleContextTest, TrackService) { int count = 0; - class ITestSvc { - public: - virtual ~ITestSvc(){}; - virtual int calc(int input) = 0; - }; - ITestSvc *svc1 = (ITestSvc*)0x100; //no ranking ITestSvc *svc2 = (ITestSvc*)0x200; //no ranking ITestSvc *svc3 = (ITestSvc*)0x300; //10 ranking http://git-wip-us.apache.org/repos/asf/celix/blob/e3339ca4/framework/include/celix/BundleContext.h ---------------------------------------------------------------------- diff --git a/framework/include/celix/BundleContext.h b/framework/include/celix/BundleContext.h index e6da72f..9d9a274 100644 --- a/framework/include/celix/BundleContext.h +++ b/framework/include/celix/BundleContext.h @@ -37,6 +37,49 @@ namespace celix { class DependencyManager; } + template<typename I> + struct ServiceUseOptions { + /* + * Service filter info + */ + const std::string &serviceName{}; //required + const std::string &versionRange{}; + const std::string &filter{}; + const std::string &lang{}; //default will be C++ + + + /* + * Callbacks + */ + std::function<void(I &svc)> use{}; + std::function<void(I &svc, const celix::Properties &props)> useWithProperties{}; + std::function<void(I &svc, const celix::Properties &props, celix::Bundle &svcOwner)> useWithOwner{}; + }; + + template<typename I> + struct ServiceTrackingOptions { + /* + * Service filter info + */ + const std::string &serviceName{}; //required + const std::string &versionRange{}; + const std::string &filter{}; + const std::string &lang{}; //default will be C++ + + std::function<void(I* svc)> set{}; + std::function<void(I* svc)> add{}; + std::function<void(I* svc)> remove{}; + + std::function<void(I* svc, const celix::Properties &props)> setWithProperties{}; + std::function<void(I* svc, const celix::Properties &props)> addWithProperties{}; + std::function<void(I* svc, const celix::Properties &props)> removeWithProperties{}; + + std::function<void(I* svc, const celix::Properties &props, const celix::Bundle &svcOwner)> setWithOwner{}; + std::function<void(I* svc, const celix::Properties &props, const celix::Bundle &svcOwner)> addWithOwner{}; + std::function<void(I* svc, const celix::Properties &props, const celix::Bundle &svcOwner)> removeWithOwner{}; + }; + + struct BundleRegistrationOptions { std::string id{}; std::string name{}; @@ -68,6 +111,7 @@ namespace celix { template<typename I> long registerCService(const std::string &serviceName, I *svc, const std::string &version = "", Properties props = {}) noexcept { + static_assert(std::is_pod<I>::value, "Service I must be a 'Plain Old Data' object"); return this->registerServiceInternal(serviceName, svc, version, celix::Constants::SERVICE_C_LANG, std::move(props)); } @@ -90,8 +134,6 @@ namespace celix { * * @param ctx The bundle context. * @param serviceName The required service name to track - * @param serviceVersionRange Optional the service version range to track. Can be empty ("") - * @param filter Optional the LDAP filter to use. Can be empty ("") * @param set is a required callback, which will be called when a new highest ranking service is set. * @return the tracker id or < 0 if unsuccessful. */ @@ -111,9 +153,6 @@ namespace celix { * * @param ctx The bundle context. * @param serviceName The required service name to track - * @param serviceVersionRange Optional the service version range to track - * @param filter Optional the LDAP filter to use - * @param callbackHandle The data pointer, which will be used in the callbacks * @param add is a required callback, which will be called when a service is added and initially for the existing service. * @param remove is a required callback, which will be called when a service is removed * @return the tracker id or < 0 if unsuccessful. @@ -141,13 +180,13 @@ namespace celix { //TODO add trackCService(s) variants /** - * Note use fucntion by const reference. Only used during the call. + * Note use function by const reference. Only used during the call. * @param serviceId * @param I * @return */ template<typename I> - bool useService(long serviceId, const std::string &/*serviceName*/ /*sanity*/, const std::function<void(I &svc, const celix::Properties &props, const celix::Bundle &svcOwner)> &/*use*/) noexcept { + bool useServiceWithId(long serviceId, const std::string &/*serviceName*/ /*sanity*/, const std::function<void(I &svc, const celix::Properties &props, const celix::Bundle &svcOwner)> &/*use*/) noexcept { std::string filter = std::string{"(service.id="} + std::to_string(serviceId) + std::string{")"}; //TODO use useServiceWithOptions return this->useService<I>(serviceName, "", filter, use); return false;
