Repository: celix Updated Branches: refs/heads/feature/CELIX-426-cxx-api 34914e827 -> d9e95de89
CELIX-446: Adds ServiceAdaptors concept, which - with the use of type erasure - make it possible to register/use C++ service which are automatically wrapper to/from their C counterpart. Project: http://git-wip-us.apache.org/repos/asf/celix/repo Commit: http://git-wip-us.apache.org/repos/asf/celix/commit/d9e95de8 Tree: http://git-wip-us.apache.org/repos/asf/celix/tree/d9e95de8 Diff: http://git-wip-us.apache.org/repos/asf/celix/diff/d9e95de8 Branch: refs/heads/feature/CELIX-426-cxx-api Commit: d9e95de8998db686ce95ebb866685f1cda3bfa89 Parents: 34914e8 Author: Pepijn Noltes <[email protected]> Authored: Mon Jul 9 21:49:16 2018 +0200 Committer: Pepijn Noltes <[email protected]> Committed: Mon Jul 9 21:49:16 2018 +0200 ---------------------------------------------------------------------- .../shell/shell/include/celix/IShellCommand.h | 45 +++ .../include/celix/ShellCommandServiceAdapters.h | 110 +++++++ bundles/shell/shell/include/command.h | 4 +- examples/celix-examples/CMakeLists.txt | 3 + .../shell_command_example/CMakeLists.txt | 30 ++ .../shell_command_example/src/Activator.cc | 58 ++++ .../shell_command_example/src/activator.c | 81 ++++++ libs/framework/gtest/CMakeLists.txt | 1 + .../gtest/src/cxx_ServiceAdapter_tests.cc | 290 +++++++++++++++++++ libs/framework/include/celix/BundleContext.h | 59 ++-- libs/framework/include/celix/ServiceAdapter.h | 148 ++++++++++ .../include/celix/impl/BundleContextImpl.h | 268 +++++++++++++---- libs/framework/src/service_tracker.c | 4 +- 13 files changed, 1020 insertions(+), 81 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/celix/blob/d9e95de8/bundles/shell/shell/include/celix/IShellCommand.h ---------------------------------------------------------------------- diff --git a/bundles/shell/shell/include/celix/IShellCommand.h b/bundles/shell/shell/include/celix/IShellCommand.h new file mode 100644 index 0000000..8ae7acc --- /dev/null +++ b/bundles/shell/shell/include/celix/IShellCommand.h @@ -0,0 +1,45 @@ +/** + *Licensed to the Apache Software Foundation (ASF) under one + *or more contributor license agreements. See the NOTICE file + *distributed with this work for additional information + *regarding copyright ownership. The ASF licenses this file + *to you under the Apache License, Version 2.0 (the + *"License"); you may not use this file except in compliance + *with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, + *software distributed under the License is distributed on an + *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + *specific language governing permissions and limitations + *under the License. + */ + +#include <string> +#include <iostream> + +#include "command.h" + +#ifndef CXX_CELIX_ISHELLCOMMAND_H +#define CXX_CELIX_ISHELLCOMMAND_H + +namespace celix { + + class IShellCommand { + public: + static constexpr const char * const COMMAND_NAME = OSGI_SHELL_COMMAND_NAME; + static constexpr const char * const COMMAND_USAGE = OSGI_SHELL_COMMAND_USAGE; + static constexpr const char * const COMMAND_DESCRIPTION = OSGI_SHELL_COMMAND_DESCRIPTION; + + virtual ~IShellCommand() = default; + virtual int executeCommand(const std::string &commandLine, std::ostream &out, std::ostream &err) = 0; + }; + +} + +#include "ShellCommandServiceAdapters.h" + + +#endif //CXX_CELIX_ISHELLCOMMAND_H http://git-wip-us.apache.org/repos/asf/celix/blob/d9e95de8/bundles/shell/shell/include/celix/ShellCommandServiceAdapters.h ---------------------------------------------------------------------- diff --git a/bundles/shell/shell/include/celix/ShellCommandServiceAdapters.h b/bundles/shell/shell/include/celix/ShellCommandServiceAdapters.h new file mode 100644 index 0000000..1b5dbcf --- /dev/null +++ b/bundles/shell/shell/include/celix/ShellCommandServiceAdapters.h @@ -0,0 +1,110 @@ +/** + *Licensed to the Apache Software Foundation (ASF) under one + *or more contributor license agreements. See the NOTICE file + *distributed with this work for additional information + *regarding copyright ownership. The ASF licenses this file + *to you under the Apache License, Version 2.0 (the + *"License"); you may not use this file except in compliance + *with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, + *software distributed under the License is distributed on an + *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + *specific language governing permissions and limitations + *under the License. + */ + +#include <sstream> + +#include "command.h" +#include "celix/ServiceAdapter.h" +#include "celix/Constants.h" + +#ifndef CXX_CELIX_SHELLCOMMANDSERVICEADAPTERS_H +#define CXX_CELIX_SHELLCOMMANDSERVICEADAPTERS_H + +namespace celix { + + + //From a C++ service to a C service when registering IShellCommand services. + class ShellServiceRegistrationAdapterFactory : public celix::IServiceRegistrationAdapterFactory<celix::IShellCommand, command_service_t> { + public: + class ServiceAdapter : public celix::IServiceAdapter<command_service_t> { + public: + ServiceAdapter(celix::IShellCommand *svc) { + cSvc.handle = static_cast<void*>(svc); + cSvc.executeCommand = [](void* handle, char* commandLine, FILE *outStream, FILE *errStream) -> celix_status_t { + auto *local_cmd = static_cast<IShellCommand*>(handle); + std::ostringstream out; + std::ostringstream err; + int status = local_cmd->executeCommand(std::string{commandLine}, out, err); + std::fprintf(outStream, "%s", out.str().c_str()); + std::fprintf(errStream, "%s", err.str().c_str()); + return status; + }; + } + virtual ~ServiceAdapter() = default; + command_service_t* adapt() override { return &cSvc; } + private: + command_service_t cSvc{}; + }; + + virtual ~ShellServiceRegistrationAdapterFactory() = default; + std::string serviceName() override { return OSGI_SHELL_COMMAND_SERVICE_NAME; } + std::string serviceVersion() override { return ""; } + std::string serviceLanguage() override { return celix::Constants::SERVICE_C_LANG; } + celix::IServiceAdapter<command_service_t>* createAdapter(celix::IShellCommand* svc) override { return new ServiceAdapter{svc}; } + }; + + celix::IServiceRegistrationAdapterFactory<celix::IShellCommand, command_service_t>& serviceRegistrationAdapterFactoryFor(celix::IShellCommand */*dummy*/) { + static ShellServiceRegistrationAdapterFactory factory{}; + return factory; + } + + //From a C to a C++ services when using IShellCommand services. + class ShellServiceUsageAdapterFactory : public celix::IServiceUsageAdapterFactory<celix::IShellCommand> { + public: + class ServiceAdapter : public celix::IServiceAdapter<celix::IShellCommand>, public celix::IShellCommand { + public: + ServiceAdapter(void *_svc) : cSvc{static_cast<command_service_t*>(_svc)} {} + virtual ~ServiceAdapter() = default; + celix::IShellCommand* adapt() override { return this; } + + int executeCommand(const std::string &commandLine, std::ostream &out, std::ostream &err) override { + char *outBuf = nullptr; + char *errBuf = nullptr; + FILE *outStream = open_memstream(&outBuf, nullptr); + FILE *errStream = open_memstream(&errBuf, nullptr); + int status = cSvc->executeCommand(cSvc->handle, (char*)commandLine.c_str(), outStream, errStream); + fflush(outStream); + fclose(outStream); + fflush(errStream); + fclose(errStream); + out << outBuf; + err << errBuf; + free(outBuf); + free(errBuf); + return status; + } + private: + command_service_t* cSvc; + }; + + virtual ~ShellServiceUsageAdapterFactory() = default; + std::string serviceName() override { return OSGI_SHELL_COMMAND_SERVICE_NAME; } + std::string serviceVersionRange() override { return ""; } + std::string serviceLanguage() override { return celix::Constants::SERVICE_C_LANG; } + celix::IServiceAdapter<celix::IShellCommand>* createAdapter(void *registeredSvc) override { return new ServiceAdapter{registeredSvc}; } + }; + + celix::IServiceUsageAdapterFactory<celix::IShellCommand>& serviceUsageAdapterFactoryFor(celix::IShellCommand* /*dummy_used_for_infer*/) { + static ShellServiceUsageAdapterFactory factory{}; + return factory; + } +} + + +#endif //CXX_CELIX_SHELLCOMMANDSERVICEADAPTERS_H http://git-wip-us.apache.org/repos/asf/celix/blob/d9e95de8/bundles/shell/shell/include/command.h ---------------------------------------------------------------------- diff --git a/bundles/shell/shell/include/command.h b/bundles/shell/shell/include/command.h index 65e4306..f856648 100644 --- a/bundles/shell/shell/include/command.h +++ b/bundles/shell/shell/include/command.h @@ -34,8 +34,8 @@ #define OSGI_SHELL_COMMAND_USAGE "command.usage" #define OSGI_SHELL_COMMAND_DESCRIPTION "command.description" -static const char * const OSGI_SHELL_COMMAND_SERVICE_NAME = "commandService"; -static const char * const OSGI_SHELL_COMMAND_SERVICE_VERSION = "1.0.0"; +#define OSGI_SHELL_COMMAND_SERVICE_NAME "commandService" +#define OSGI_SHELL_COMMAND_SERVICE_VERSION "1.0.0" typedef struct commandService command_service_t; typedef command_service_t * command_service_pt; http://git-wip-us.apache.org/repos/asf/celix/blob/d9e95de8/examples/celix-examples/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/examples/celix-examples/CMakeLists.txt b/examples/celix-examples/CMakeLists.txt index c313e8e..7edfd30 100644 --- a/examples/celix-examples/CMakeLists.txt +++ b/examples/celix-examples/CMakeLists.txt @@ -37,4 +37,7 @@ if (EXAMPLES) add_subdirectory(service_hook_example) add_subdirectory(log_service_example) + + add_subdirectory(shell_command_example) + endif(EXAMPLES) http://git-wip-us.apache.org/repos/asf/celix/blob/d9e95de8/examples/celix-examples/shell_command_example/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/examples/celix-examples/shell_command_example/CMakeLists.txt b/examples/celix-examples/shell_command_example/CMakeLists.txt new file mode 100644 index 0000000..467bc0d --- /dev/null +++ b/examples/celix-examples/shell_command_example/CMakeLists.txt @@ -0,0 +1,30 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +add_celix_bundle(shell_example_c SOURCES src/activator.c VERSION 1.0.0) +target_link_libraries(shell_example_c PRIVATE Celix::shell_api) + +add_celix_bundle(shell_example_cxx SOURCES src/Activator.cc VERSION 1.0.0) +target_link_libraries(shell_example_cxx PRIVATE Celix::shell_api) + +add_celix_container(shell_example + BUNDLES + Celix::shell + Celix::shell_tui + shell_example_c + shell_example_cxx +) \ No newline at end of file http://git-wip-us.apache.org/repos/asf/celix/blob/d9e95de8/examples/celix-examples/shell_command_example/src/Activator.cc ---------------------------------------------------------------------- diff --git a/examples/celix-examples/shell_command_example/src/Activator.cc b/examples/celix-examples/shell_command_example/src/Activator.cc new file mode 100644 index 0000000..75835b0 --- /dev/null +++ b/examples/celix-examples/shell_command_example/src/Activator.cc @@ -0,0 +1,58 @@ +/** + *Licensed to the Apache Software Foundation (ASF) under one + *or more contributor license agreements. See the NOTICE file + *distributed with this work for additional information + *regarding copyright ownership. The ASF licenses this file + *to you under the Apache License, Version 2.0 (the + *"License"); you may not use this file except in compliance + *with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, + *software distributed under the License is distributed on an + *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + *specific language governing permissions and limitations + *under the License. + */ + +#include <iostream> +#include <thread> +#include <atomic> +#include <vector> + +#include "celix/BundleActivator.h" +#include "celix/IShellCommand.h" + +namespace { + class BundleActivator : public celix::IBundleActivator, public celix::IShellCommand { + public: + celix_status_t start(celix::BundleContext &_ctx) override { + this->ctx = &_ctx; + celix::Properties props{}; + props[OSGI_SHELL_COMMAND_NAME] = "cxx_exmpl"; + svcId = ctx->registerService<celix::IShellCommand>(this, props); + return CELIX_SUCCESS; + } + + celix_status_t stop(celix::BundleContext &ctx) override { + ctx.unregisterService(svcId); + return CELIX_SUCCESS; + } + + virtual int executeCommand(const std::string &, std::ostream &out, std::ostream &) override { + ctx->useServices<celix::IShellCommand>([&out](celix::IShellCommand &) { + out << "found a IShellCommandService" << std::endl; + //TODO use useServicesWithOptionts -> useWithProperties ! + }); + return 0; + } + + private: + celix::BundleContext *ctx; + long svcId{-1}; + }; +} + +CELIX_GEN_CXX_BUNDLE_ACTIVATOR(BundleActivator) http://git-wip-us.apache.org/repos/asf/celix/blob/d9e95de8/examples/celix-examples/shell_command_example/src/activator.c ---------------------------------------------------------------------- diff --git a/examples/celix-examples/shell_command_example/src/activator.c b/examples/celix-examples/shell_command_example/src/activator.c new file mode 100644 index 0000000..ca42deb --- /dev/null +++ b/examples/celix-examples/shell_command_example/src/activator.c @@ -0,0 +1,81 @@ +/** + *Licensed to the Apache Software Foundation (ASF) under one + *or more contributor license agreements. See the NOTICE file + *distributed with this work for additional information + *regarding copyright ownership. The ASF licenses this file + *to you under the Apache License, Version 2.0 (the + *"License"); you may not use this file except in compliance + *with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, + *software distributed under the License is distributed on an + *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + *specific language governing permissions and limitations + *under the License. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "celix_bundle_activator.h" +#include "constants.h" + +#include "command.h" + +typedef struct activator_data { + celix_bundle_context_t *ctx; + command_service_t svc; + long svcId; +} activator_data_t; + +struct useShellServiceHandle { + activator_data_t* data; + FILE *out; + int count; +}; + +static void useShellService(void *handle, void *_svc __attribute__((unused)), const celix_properties_t *props) { + struct useShellServiceHandle *useHandle = handle; + const char *name = celix_properties_getWithDefault(props, OSGI_SHELL_COMMAND_NAME, "Not Found"); + fprintf(useHandle->out, "%i: Command %s found\n", useHandle->count++, name); +} + +static celix_status_t executeCommand(void *handle, char * commandLine, FILE *outStream, FILE *errorStream) { + activator_data_t *data = handle; + struct useShellServiceHandle useHandle = { .data = data, .out = outStream, .count = 0}; + + celix_service_use_options_t opts = CELIX_EMPTY_SERVICE_USE_OPTIONS; + opts.filter.serviceName = OSGI_SHELL_COMMAND_SERVICE_NAME; + opts.useWithProperties = useShellService; + opts.callbackHandle = &useHandle; + celix_bundleContext_useServicesWithOptions(data->ctx, &opts); + fprintf(outStream, "Found %i command_service_t services\n", useHandle.count); + + return CELIX_SUCCESS; +} + + +static celix_status_t activator_start(activator_data_t *data, celix_bundle_context_t *ctx) { + data->ctx = ctx; + data->svc.handle = data; + data->svc.executeCommand = executeCommand; + + data->svcId = -1L; + celix_properties_t *props = celix_properties_create(); + celix_properties_set(props, OSGI_SHELL_COMMAND_NAME, "c_exmpl"); + data->svcId = celix_bundleContext_registerService(ctx, &data->svc, OSGI_SHELL_COMMAND_SERVICE_NAME, props); + printf("Registered command service with service id %li\n", data->svcId); + + return CELIX_SUCCESS; +} + +static celix_status_t activator_stop(activator_data_t *data, celix_bundle_context_t *ctx) { + celix_bundleContext_unregisterService(ctx, data->svcId); + printf("Unregistered command service with service id %li\n", data->svcId); + return CELIX_SUCCESS; +} + +CELIX_GEN_BUNDLE_ACTIVATOR(activator_data_t, activator_start, activator_stop) \ No newline at end of file http://git-wip-us.apache.org/repos/asf/celix/blob/d9e95de8/libs/framework/gtest/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/libs/framework/gtest/CMakeLists.txt b/libs/framework/gtest/CMakeLists.txt index 4ecd47a..26af024 100644 --- a/libs/framework/gtest/CMakeLists.txt +++ b/libs/framework/gtest/CMakeLists.txt @@ -21,6 +21,7 @@ set(SOURCES src/cxx_FrameworkFactory_tests.cc src/cxx_BundleContext_tests.cc src/cxx_Bundle_tests.cc + src/cxx_ServiceAdapter_tests.cc ) add_executable(cxx_framework_tests ${SOURCES}) target_link_libraries(cxx_framework_tests PRIVATE gtest Celix::framework) http://git-wip-us.apache.org/repos/asf/celix/blob/d9e95de8/libs/framework/gtest/src/cxx_ServiceAdapter_tests.cc ---------------------------------------------------------------------- diff --git a/libs/framework/gtest/src/cxx_ServiceAdapter_tests.cc b/libs/framework/gtest/src/cxx_ServiceAdapter_tests.cc new file mode 100644 index 0000000..63add3a --- /dev/null +++ b/libs/framework/gtest/src/cxx_ServiceAdapter_tests.cc @@ -0,0 +1,290 @@ +/** + *Licensed to the Apache Software Foundation (ASF) under one + *or more contributor license agreements. See the NOTICE file + *distributed with this work for additional information + *regarding copyright ownership. The ASF licenses this file + *to you under the Apache License, Version 2.0 (the + *"License"); you may not use this file except in compliance + *with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, + *software distributed under the License is distributed on an + *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + *specific language governing permissions and limitations + *under the License. + */ + +#include "gtest/gtest.h" + +#include "celix/FrameworkFactory.h" + +class ServiceAdapterTest : public ::testing::Test { +public: + ServiceAdapterTest() { + celix::Properties config{}; + config["org.osgi.framework.storage.clean"] = "onFirstInit"; + config["org.osgi.framework.storage"] = "test-cache"; //TODO tmp dir? + this->fw_ptr = std::unique_ptr<celix::Framework>{celix::FrameworkFactory::newFramework(std::move(config))}; + } + + ~ServiceAdapterTestd(){} + + celix::Framework& framework() { return *(this->fw_ptr); } +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; } +}; + + +/*This service can be used with the DefaulServiceWrapper*/ +class IHelloService { +public: + static constexpr const char * const SERVICE_NAME = "IHelloService"; + static constexpr const char * const SERVICE_VERSION = "1.0.0"; + + virtual ~IHelloService() = default; + virtual std::string hello() = 0; +}; + +TEST_F(ServiceAdapterTest, RegisterServiceWithWrapperTest) { + auto &ctx = this->framework().getFrameworkContext(); + + class HelloServiceImpl : public IHelloService { + public: + virtual ~HelloServiceImpl() = default; + std::string hello() override { + return std::string{"hello1"}; + } + }; + + HelloServiceImpl svc{}; + IHelloService* svcPointer = &svc; + long svcId = ctx.registerService<IHelloService>(svcPointer); + + //use without wrapper + bool called; + called = ctx.useService<IHelloService>(IHelloService::SERVICE_NAME, [](IHelloService &svc) { + ASSERT_EQ(std::string{"hello1"}, svc.hello()); + }); + ASSERT_TRUE(called); + + //use with wrapper + called = ctx.useService<IHelloService>([](IHelloService &svc) { + auto result = svc.hello(); + ASSERT_EQ(std::string{"hello1"}, result); + }); + ASSERT_TRUE(called); + + ctx.unregisterService(svcId); +} + +/* Because this service has no SERVICE_NAME/SERVICE_VERSION is can not be wrapped by the DefaultServiceWrapper. + * Custom wrapper is needed to achieve the same effect + */ +class IHelloServiceNoDefaultWrapper { +public: + virtual ~IHelloServiceNoDefaultWrapper() = default; + virtual std::string hello() = 0; +}; + +class HelloServiceRegistrationAdapter : public celix::IServiceRegistrationAdapterFactory<IHelloServiceNoDefaultWrapper, IHelloServiceNoDefaultWrapper> { +public: + class ServiceAdapter : public celix::IServiceAdapter<IHelloServiceNoDefaultWrapper> { + public: + ServiceAdapter(IHelloServiceNoDefaultWrapper *_svc) : svc{_svc} {} + virtual ~ServiceAdapter() = default; + IHelloServiceNoDefaultWrapper* adapt() override { return svc; } + private: + IHelloServiceNoDefaultWrapper* svc; + }; + + virtual ~HelloServiceRegistrationAdapter() = default; + std::string serviceName() override { return "HelloService"; } + std::string serviceVersion() override { return "1.0.0"; } + std::string serviceLanguage() override { return celix::Constants::SERVICE_CXX_LANG; } + celix::IServiceAdapter<IHelloServiceNoDefaultWrapper>* createAdapter(IHelloServiceNoDefaultWrapper *svc) override { return new ServiceAdapter{svc}; } +}; + +celix::IServiceRegistrationAdapterFactory<IHelloServiceNoDefaultWrapper,IHelloServiceNoDefaultWrapper>& serviceRegistrationAdapterFactoryFor(IHelloServiceNoDefaultWrapper */*dummy*/) { + static HelloServiceRegistrationAdapter factory{}; + return factory; +} + +class HelloServiceUsageAdapter : public celix::IServiceUsageAdapterFactory<IHelloServiceNoDefaultWrapper> { +public: + class ServiceAdapter : public celix::IServiceAdapter<IHelloServiceNoDefaultWrapper> { + public: + ServiceAdapter(void *_svc) : svc{static_cast<IHelloServiceNoDefaultWrapper*>(_svc)} {} + virtual ~ServiceAdapter() = default; + IHelloServiceNoDefaultWrapper* adapt() override { return svc; } + private: + IHelloServiceNoDefaultWrapper* svc; + }; + + virtual ~HelloServiceUsageAdapter() = default; + std::string serviceName() override { return "HelloService"; } + std::string serviceVersionRange() override { return "[1,2)"; } + std::string serviceLanguage() override { return celix::Constants::SERVICE_CXX_LANG; } + celix::IServiceAdapter<IHelloServiceNoDefaultWrapper>* createAdapter(void *registeredSvc) override { return new ServiceAdapter(registeredSvc); } +}; + + + +celix::IServiceUsageAdapterFactory<IHelloServiceNoDefaultWrapper>& serviceUsageAdapterFactoryFor(IHelloServiceNoDefaultWrapper*) { + static HelloServiceUsageAdapter factory{}; + return factory; +} + +TEST_F(ServiceAdapterTest, RegisterServiceWithCustomWrapperTest) { + auto &ctx = this->framework().getFrameworkContext(); + + class HelloServiceImpl : public IHelloServiceNoDefaultWrapper { + public: + virtual ~HelloServiceImpl() = default; + std::string hello() override { + return std::string{"hello1"}; + } + }; + + HelloServiceImpl svc{}; + long svcId = ctx.registerService<IHelloServiceNoDefaultWrapper>(&svc); + + //use without wrapper + bool called = ctx.useService<IHelloServiceNoDefaultWrapper>("HelloService", [](IHelloServiceNoDefaultWrapper &svc) { + ASSERT_EQ(std::string{"hello1"}, svc.hello()); + }); + ASSERT_TRUE(called); + + //use with wrapper + called = ctx.useService<IHelloServiceNoDefaultWrapper>([](IHelloServiceNoDefaultWrapper &svc) { + ASSERT_EQ(std::string{"hello1"}, svc.hello()); + }); + ASSERT_TRUE(called); + + ctx.unregisterService(svcId); +} + +#define HELLO_SERVICE_NAME "hello_service" +#define HELLO_SERVICE_VERSION "1.0.0" + +//The underlining c service +typedef struct do_service { + void *handle; + int (*do_something)(void* handle); +} do_service_t; + +//The C++ service to use in the C++ context +class IDoService { +public: + virtual ~IDoService() = default; + virtual int do_something() = 0; +}; + +class DoServiceToCWrapper : public celix::IServiceRegistrationAdapterFactory<IDoService, do_service_t> { +public: + class ServiceAdapter : public celix::IServiceAdapter<do_service_t> { + public: + ServiceAdapter(IDoService *svc) { + cSvc.handle = static_cast<void*>(svc); + cSvc.do_something = [](void *handle) -> int { + auto* s = static_cast<IDoService*>(handle); + return s->do_something(); + }; + } + virtual ~ServiceAdapter() = default; + do_service_t* adapt() override { return &cSvc; } + private: + do_service_t cSvc{}; + }; + + virtual ~DoServiceToCWrapper() = default; + std::string serviceName() override { return "do_service"; } + std::string serviceVersion() override { return "1.0.0"; } + std::string serviceLanguage() override { return celix::Constants::SERVICE_C_LANG; } + celix::IServiceAdapter<do_service_t>* createAdapter(IDoService* svc) override { return new ServiceAdapter{svc}; } +}; + +celix::IServiceRegistrationAdapterFactory<IDoService, do_service_t>& serviceRegistrationAdapterFactoryFor(IDoService */*dummy*/) { + static DoServiceToCWrapper factory{}; + return factory; +} + +class DoServiceFromCWrapper : public celix::IServiceUsageAdapterFactory<IDoService> { +public: + class ServiceAdapter : public celix::IServiceAdapter<IDoService>, public IDoService { + public: + ServiceAdapter(void *_svc) : cSvc{static_cast<do_service_t*>(_svc)} {} + virtual ~ServiceAdapter() = default; + IDoService* adapt() override { return this; } + + int do_something() override { + return cSvc->do_something(cSvc->handle); + } + private: + do_service_t* cSvc; + }; + + virtual ~DoServiceFromCWrapper() = default; + std::string serviceName() override { return "do_service"; } + std::string serviceVersionRange() override { return "[1,2)"; } + std::string serviceLanguage() override { return celix::Constants::SERVICE_C_LANG; } + celix::IServiceAdapter<IDoService>* createAdapter(void *registeredSvc) override { return new ServiceAdapter{registeredSvc}; } +}; + +celix::IServiceUsageAdapterFactory<IDoService>& serviceUsageAdapterFactoryFor(IDoService* /*dummy_used_for_infer*/) { + static DoServiceFromCWrapper factory{}; + return factory; +} + +TEST_F(ServiceAdapterTest, RegisterServiceWithCxxToCWrapperTest) { + auto &ctx = this->framework().getFrameworkContext(); + + class DoServiceImpl : public IDoService { + public: + virtual ~DoServiceImpl() = default; + int do_something() override { + return 42; + } + }; + + DoServiceImpl svc{}; + long svcId = ctx.registerService<IDoService>(&svc); + ASSERT_TRUE(svcId >= 0); + //TODO assert properties has C as svc lang + + bool called = ctx.useService<IDoService>([](IDoService &svc) { + int result = svc.do_something(); + ASSERT_EQ(42, result); + }); + ASSERT_TRUE(called); + + + called = false; + long trkId = ctx.trackService<IDoService>([&](IDoService *svc) { + called = true; + ASSERT_EQ(42, svc->do_something()); + }); + ASSERT_TRUE(trkId >= 0); + ASSERT_TRUE(called); + ctx.stopTracker(trkId); + + ctx.unregisterService(svcId); +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/celix/blob/d9e95de8/libs/framework/include/celix/BundleContext.h ---------------------------------------------------------------------- diff --git a/libs/framework/include/celix/BundleContext.h b/libs/framework/include/celix/BundleContext.h index 319cbfb..b3ce0b4 100644 --- a/libs/framework/include/celix/BundleContext.h +++ b/libs/framework/include/celix/BundleContext.h @@ -26,6 +26,7 @@ #include "celix/Properties.h" #include "celix/Bundle.h" #include "celix/IServiceFactory.h" +#include "celix/ServiceAdapter.h" #ifndef CXX_CELIX_BUNDLECONTEXT_H #define CXX_CELIX_BUNDLECONTEXT_H @@ -138,6 +139,10 @@ namespace celix { BundleContext(celix_bundle_context_t *ctx, celix::Framework& fw); //TODO hide somehow ... friend ? virtual ~BundleContext(); + //TODO document, only possible if a service wrapper is available + template<typename I> + long registerService(I *svcWithWrapper, celix::Properties props = {}) noexcept; + template<typename I> long registerService(I *svc, const std::string &serviceName, celix::Properties props = {}) noexcept; @@ -172,6 +177,19 @@ namespace celix { long trackService(const std::string &serviceName, std::function<void(I *svc)> set) noexcept; /** + * track service for the provided service type using a ServiceUsageAdaptor. + * The highest ranking services will used for the callback. + * If a new and higher ranking services the callback with be called again with the new service. + * If a service is removed a the callback with be called with next highest ranking service or NULL as service. + * + * @param serviceName The required service name to track + * @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. + */ + template<typename I> + long trackService(std::function<void(I *svc)> set) noexcept; + + /** * track services for the provided serviceName and/or filter. * * @param serviceName The required service name to track @@ -193,22 +211,35 @@ namespace celix { template<typename I> long trackServicesWithOptions(const celix::ServiceTrackingOptions<I>& opts) noexcept; - - /** - * Note use function by const reference. Only used during the call. - * @param serviceId - * @param I - * @return - */ + //TODO remove if WithOptions is available template<typename I> 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; template<typename I> + bool useServiceWithId(long serviceId, const std::string &/*serviceName*/ /*sanity*/, const std::function<void(I &svc)> &use) noexcept; + + //TODO remove if WithOptions is available + template<typename I> bool useService(const std::string &serviceName, const std::function<void(I &svc, const celix::Properties &props, const celix::Bundle &svcOwner)> &use) noexcept; template<typename I> + bool useService(const std::string &serviceName, const std::function<void(I &svc)> &use) noexcept; + + //TODO remove if WithOptions is available + template<typename I> void useServices(const std::string &serviceName, const std::function<void(I &svc, const celix::Properties &props, const celix::Bundle &svcOwner)> &use) noexcept; + template<typename I> + void useServices(const std::string &serviceName, const std::function<void(I &svc)> &use) noexcept; + + //TODO rest of variants with service & wrapper for useServiceWithId, useService and useServices + template<typename I> + bool useService(const std::function<void(I &svc)> &use) noexcept; + + template<typename I> + void useServices(const std::function<void(I &svc)> &use) noexcept; + + //TODO add useService(s)WithOptions //TODO add useCService(s) variants @@ -243,20 +274,6 @@ namespace celix { celix::dm::DependencyManager& getDependencyManager() noexcept; - /** TODO - long registerEmbeddedBundle( - std::string id, - std::function<void(celix::BundleContext& ctx)> start, - std::function<void(celix::BundleContext& ctx)> stop, - celix::Properties manifest = {}, - bool autoStart = true - ) noexcept; - - void registerEmbeddedBundle(const celix::BundleRegistrationOptions &opts) noexcept = 0; - */ - - - long installBundle(const std::string &bundleLocation, bool autoStart = true) noexcept; void useBundles(const std::function<void(const celix::Bundle &bnd)> &use) noexcept; http://git-wip-us.apache.org/repos/asf/celix/blob/d9e95de8/libs/framework/include/celix/ServiceAdapter.h ---------------------------------------------------------------------- diff --git a/libs/framework/include/celix/ServiceAdapter.h b/libs/framework/include/celix/ServiceAdapter.h new file mode 100644 index 0000000..7030b7d --- /dev/null +++ b/libs/framework/include/celix/ServiceAdapter.h @@ -0,0 +1,148 @@ +/** + *Licensed to the Apache Software Foundation (ASF) under one + *or more contributor license agreements. See the NOTICE file + *distributed with this work for additional information + *regarding copyright ownership. The ASF licenses this file + *to you under the Apache License, Version 2.0 (the + *"License"); you may not use this file except in compliance + *with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + *Unless required by applicable law or agreed to in writing, + *software distributed under the License is distributed on an + *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + *specific language governing permissions and limitations + *under the License. + */ + +#include <string> + +#include "celix/Constants.h" + +#ifndef CXX_CELIX_SERIVCEADAPTOR_H +#define CXX_CELIX_SERIVCEADAPTOR_H + +namespace celix { + + class IServiceAdapterFactoryBase { + public: + virtual ~IServiceAdapterFactoryBase() = default; + virtual std::string serviceName() = 0; //TODO make const std::string? + virtual std::string serviceLanguage() = 0; + + }; + + class IServiceAdapterBase { + public: + virtual ~IServiceAdapterBase() = default; + }; + + template<typename I> + class IServiceAdapter : public IServiceAdapterBase { + public: + virtual ~IServiceAdapter() = default; + virtual I* adapt() = 0; + }; + + template<typename I /*from*/, typename T /*to -> register version*/> + class IServiceRegistrationAdapterFactory : public IServiceAdapterFactoryBase { + public: + using fromType = I; + using registeringType = T; + virtual ~IServiceRegistrationAdapterFactory() = default; + virtual std::string serviceVersion() = 0; + virtual IServiceAdapter<T>* createAdapter(I *fromSvc) = 0; + }; + + template<typename I> + class IServiceUsageAdapterFactory : public IServiceAdapterFactoryBase { + public: + using type = I; + virtual ~IServiceUsageAdapterFactory() = default; + virtual std::string serviceVersionRange() = 0; + virtual IServiceAdapter<I>* createAdapter(void *registeredSvc) = 0; + }; + + /** + * The default Service Registration Adaptor. This wrapper assumes that a SERVICE_NAME, SERVICE_VERSION is available. + * It also assumes the service is a C++ service for registering and for using. + * + * It is valid to create the wrapper with a nulllptr svc object, but then the servicePointer() method result + * will also be a nullptr. + * + * @tparam I The C++ Service Type + */ + template<typename I> + class DefaultServiceRegistrationAdapterFactory : public IServiceRegistrationAdapterFactory<I, I> { + public: + class DefaultServiceAdapter : public IServiceAdapter<I> { + public: + DefaultServiceAdapter(I *_svc) : svc{_svc} {} + virtual ~DefaultServiceAdapter() = default; + I* adapt() override { return svc; } + private: + I* svc; + }; + + virtual ~DefaultServiceRegistrationAdapterFactory() = default; + std::string serviceName() override { return I::SERVICE_NAME; } + std::string serviceVersion() override { return I::SERVICE_VERSION; } + std::string serviceLanguage() override { return celix::Constants::SERVICE_CXX_LANG; } + IServiceAdapter<I>* createAdapter(I* svc) override { return new DefaultServiceAdapter{svc}; } + }; + + + template<typename I> + IServiceRegistrationAdapterFactory<I,I>& serviceRegistrationAdapterFactoryFor(I */*dummy for infer*/) { + static DefaultServiceRegistrationAdapterFactory<I> factory{}; + return factory; + } + + /** + * The default Service Usage Adaptor. This wrapper assumes that a SERVICE_NAME, SERVICE_VERSION is available. + * It also assumes the service is a registered C++ service to be used for C++. + * + * @tparam I The C++ Service Type + */ + template<typename I> + class DefaultServiceUsageAdapterFactory : public IServiceUsageAdapterFactory<I> { + public: + class DefaultServiceAdapter : public IServiceAdapter<I> { + public: + DefaultServiceAdapter(void *_svc) : svc{static_cast<I*>(_svc)} {} + virtual ~DefaultServiceAdapter() = default; + I* adapt() override { return svc; } + private: + I* svc; + }; + + virtual ~DefaultServiceUsageAdapterFactory() = default; + std::string serviceName() override { return I::SERVICE_NAME; } + std::string serviceVersionRange() override { return ""; /* TODO std::string{"["} + I::SERVICE_VERSION + std::string{"]"};*/ } + std::string serviceLanguage() override { return celix::Constants::SERVICE_CXX_LANG; } + IServiceAdapter<I>* createAdapter(void *registeredSvc) override { return new DefaultServiceAdapter{registeredSvc}; } + }; + + template<typename I> + IServiceUsageAdapterFactory<I>& serviceUsageAdapterFactoryFor(I* /*dummy_used_for_infer*/) { + static DefaultServiceUsageAdapterFactory<I> factory{}; + return factory; + } + + /* TODO enable/improve for a better error message when function using adaptors are used, but no adaptor can be created + void* createServiceUsageAdapter(void *) { + static_assert(false, "No matching createServiceUsageAdaptor found. Please provide one!"); + return nullptr; + } + + void* createServiceRegistrationAdapter(void *) { + static_assert(false, "No matching createServiceRegistrationAdaptor found. Please provide one!"); + return nullptr; + } + */ + +} + +#endif //CXX_CELIX_SERIVCEADAPTOR_H http://git-wip-us.apache.org/repos/asf/celix/blob/d9e95de8/libs/framework/include/celix/impl/BundleContextImpl.h ---------------------------------------------------------------------- diff --git a/libs/framework/include/celix/impl/BundleContextImpl.h b/libs/framework/include/celix/impl/BundleContextImpl.h index 67f079b..85269d7 100644 --- a/libs/framework/include/celix/impl/BundleContextImpl.h +++ b/libs/framework/include/celix/impl/BundleContextImpl.h @@ -56,12 +56,16 @@ namespace celix { celix_service_factory_t factory = {nullptr, nullptr, nullptr}; celix_service_registration_options_t cOpts = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; + std::unique_ptr<IServiceAdapterBase> adapter{nullptr}; }; struct ServiceTrackingEntry { celix_service_tracking_options_t cOpts{{nullptr, nullptr, nullptr, nullptr}, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; std::unique_ptr<ServiceTrackingEntryFunctions> functions{nullptr}; + std::unique_ptr<celix::IServiceAdapterFactoryBase> adapter{nullptr}; + std::map<void*, std::unique_ptr<celix::IServiceAdapterBase>> setAdaptersCache{}; + std::map<void*, std::unique_ptr<celix::IServiceAdapterBase>> addAndRemoveAdaptersCache{}; }; } @@ -81,13 +85,16 @@ namespace celix { celix::dm::DependencyManager dm; std::mutex mutex{}; - std::map<long,celix::impl::ServiceTrackingEntry> trackingEntries{}; + std::map<long,std::unique_ptr<celix::impl::ServiceTrackingEntry>> trackingEntries{}; std::map<long,celix::impl::ServiceRegistrationEntry> registrationEntries{}; long registerServiceInternal(celix::impl::ServiceRegistrationEntry &&entry) noexcept; - long trackServicesInternal(celix::impl::ServiceTrackingEntry &&entry) noexcept; - bool useServiceInternal(const std::string &serviceName, const std::function<void(void *svc, const celix::Properties &props, const celix::Bundle &svcOwner)> &use) noexcept; - void useServicesInternal(const std::string &serviceName, const std::function<void(void *svc, const celix::Properties &props, const celix::Bundle &svcOwner)> &use) noexcept; + + template<typename I> + long trackServicesInternal(const celix::ServiceTrackingOptions<I> &opts, celix::IServiceUsageAdapterFactory<I> *factory); + long trackServicesInternal(std::unique_ptr<celix::impl::ServiceTrackingEntry> entry) noexcept; + bool useServiceInternal(const std::string &serviceName, const std::string &serviceVersionRange, const std::string &serviceLang, const std::function<void(void *svc, const celix::Properties &props, const celix::Bundle &svcOwner)> &use) noexcept; + void useServicesInternal(const std::string &serviceName, const std::string &serviceVersionRange, const std::string &serviceLang, const std::function<void(void *svc, const celix::Properties &props, const celix::Bundle &svcOwner)> &use) noexcept; }; } @@ -239,8 +246,8 @@ inline long celix::BundleContext::Impl::registerServiceInternal(celix::impl::Ser return svcId; } -inline long celix::BundleContext::Impl::trackServicesInternal(celix::impl::ServiceTrackingEntry &&entry) noexcept { - long trkId = celix_bundleContext_trackServicesWithOptions(this->c_ctx, &entry.cOpts); +inline long celix::BundleContext::Impl::trackServicesInternal(std::unique_ptr<celix::impl::ServiceTrackingEntry> entry) noexcept { + long trkId = celix_bundleContext_trackServicesWithOptions(this->c_ctx, &entry->cOpts); if (trkId >= 0) { std::lock_guard<std::mutex> lock{this->mutex}; this->trackingEntries[trkId] = std::move(entry); @@ -250,6 +257,8 @@ inline long celix::BundleContext::Impl::trackServicesInternal(celix::impl::Servi inline bool celix::BundleContext::Impl::useServiceInternal( const std::string &serviceName, + const std::string &serviceVersionRange, + const std::string &serviceLang, const std::function<void(void *svc, const celix::Properties &props, const celix::Bundle &svcOwner)> &use) noexcept { auto c_use = [](void *handle, void *svc, const celix_properties_t *c_props, const celix_bundle_t *c_svcOwner) { auto *fn = static_cast<const std::function<void(void *svc, const celix::Properties &props, const celix::Bundle &svcOwner)> *>(handle); @@ -263,7 +272,8 @@ inline bool celix::BundleContext::Impl::useServiceInternal( std::memset(&opts, 0, sizeof(opts)); opts.filter.serviceName = serviceName.empty() ? nullptr : serviceName.c_str();; - opts.filter.serviceLanguage = celix::Constants::SERVICE_CXX_LANG; + opts.filter.versionRange = serviceVersionRange.empty() ? nullptr : serviceVersionRange.c_str(); + opts.filter.serviceLanguage = serviceLang.empty() ? celix::Constants::SERVICE_CXX_LANG : serviceLang.c_str(); opts.callbackHandle = (void*)&use; opts.useWithOwner = c_use; @@ -272,6 +282,8 @@ inline bool celix::BundleContext::Impl::useServiceInternal( inline void celix::BundleContext::Impl::useServicesInternal( const std::string &serviceName, + const std::string &serviceVersionRange, + const std::string &serviceLang, const std::function<void(void *svc, const celix::Properties &props, const celix::Bundle &svcOwner)> &use) noexcept { auto c_use = [](void *handle, void *svc, const celix_properties_t *c_props, const celix_bundle_t *c_svcOwner) { auto *fn = static_cast<const std::function<void(void *svc, const celix::Properties &props, const celix::Bundle &svcOwner)> *>(handle); @@ -285,13 +297,36 @@ inline void celix::BundleContext::Impl::useServicesInternal( std::memset(&opts, 0, sizeof(opts)); opts.filter.serviceName = serviceName.empty() ? nullptr : serviceName.c_str();; - opts.filter.serviceLanguage = celix::Constants::SERVICE_CXX_LANG; + opts.filter.versionRange = serviceVersionRange.empty() ? nullptr : serviceVersionRange.c_str(); + opts.filter.serviceLanguage = serviceLang.empty() ? celix::Constants::SERVICE_CXX_LANG : serviceLang.c_str(); opts.callbackHandle = (void*)&use; opts.useWithOwner = c_use; celix_bundleContext_useServicesWithOptions(this->c_ctx, &opts); } +template<typename I> +long celix::BundleContext::registerService(I *svc, Properties props) noexcept { + using namespace celix; + auto &factory = serviceRegistrationAdapterFactoryFor(svc /*note svc value not used, just the pointer type*/); + auto *adapter = factory.createAdapter(svc); + + celix_properties_t *c_props = celix_properties_create(); + for (auto &pair : props) { + celix_properties_set(c_props, pair.first.c_str(), pair.second.c_str()); + } + + celix::impl::ServiceRegistrationEntry re{}; + re.cOpts = CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS; + re.cOpts.svc = static_cast<void*>(adapter->adapt()); + re.cOpts.serviceName = factory.serviceName().c_str(); + re.cOpts.serviceVersion = factory.serviceVersion().c_str(); + re.cOpts.serviceLanguage = factory.serviceLanguage().c_str(); + re.cOpts.properties = c_props; + re.adapter = std::unique_ptr<IServiceAdapterBase>{adapter}; + + return this->pimpl->registerServiceInternal(std::move(re)); +} template<typename I> long celix::BundleContext::registerService(I *svc, const std::string &serviceName, Properties props) noexcept { @@ -368,6 +403,19 @@ long celix::BundleContext::registerServiceWithOptions(const celix::ServiceRegist } template<typename I> +long celix::BundleContext::trackService(std::function<void(I *svc)> set) noexcept { + using namespace celix; + I* dummy = nullptr; + auto &factory = serviceUsageAdapterFactoryFor(dummy); + + celix::ServiceTrackingOptions<I> opts{factory.serviceName()}; + opts.filter.serviceLanguage = factory.serviceLanguage(); + opts.filter.versionRange = factory.serviceVersionRange(); + opts.set = set; + return this->pimpl->trackServicesInternal<I>(opts, &factory); +} + +template<typename I> long celix::BundleContext::trackService(const std::string &serviceName, std::function<void(I *svc)> set) noexcept { celix::ServiceTrackingOptions<I> opts{serviceName}; opts.set = std::move(set); @@ -384,18 +432,70 @@ long celix::BundleContext::trackServices(const std::string &serviceName, } template<typename I> -long celix::BundleContext::trackServicesWithOptions(const celix::ServiceTrackingOptions<I>& opts) { - celix::impl::ServiceTrackingEntry entry{}; - entry.functions = std::unique_ptr<celix::impl::ServiceTrackingEntryFunctions>{new celix::impl::ServiceTrackingEntryFunctions()}; +long celix::BundleContext::trackServicesWithOptions(const celix::ServiceTrackingOptions<I> &opts) { + return this->pimpl->trackServicesInternal<I>(opts, nullptr); +} + +template<typename I> +static I* svcFor(celix::IServiceUsageAdapterFactory<I> *factory, std::map<void*, std::unique_ptr<celix::IServiceAdapterBase>> &cache, void *svc) { + I* result = nullptr; + if (svc != nullptr) { + if (factory != nullptr) { + //checking cache + auto it = cache.find(svc); + if (it != cache.end()) { + celix::IServiceAdapterBase *base = it->second.get(); + auto *adapter = static_cast<celix::IServiceAdapter<I>*>(base); + result = adapter->adapt(); + } else { + celix::IServiceAdapter<I> *adapter = factory->createAdapter(svc); + cache[svc] = std::unique_ptr<celix::IServiceAdapterBase>{adapter}; + result = adapter->adapt(); + } + } else { + result = static_cast<I*>(svc); + } + } + return result; +} + +static inline void updateSetCache(std::map<void*, std::unique_ptr<celix::IServiceAdapterBase>> &cache, void *currentSetSvc) { + if (currentSetSvc == nullptr) { + cache.clear(); + } else { + auto it = cache.begin(); + while (it != cache.end()) { + if (it->first != currentSetSvc) { + cache.erase(it++); + } else { + ++it; + } + } + } +} + +static inline void removeFromCache(std::map<void*, std::unique_ptr<celix::IServiceAdapterBase>> &cache, void *removedSvc) { + auto it = cache.find(removedSvc); + if (it != cache.end()) { + cache.erase(it); + } +} + +template<typename I> +long celix::BundleContext::Impl::trackServicesInternal(const celix::ServiceTrackingOptions<I> &opts, celix::IServiceUsageAdapterFactory<I> *factory) { + auto entry = std::unique_ptr<celix::impl::ServiceTrackingEntry>{new celix::impl::ServiceTrackingEntry()}; + auto *entryPtr = entry.get(); + entry->functions = std::unique_ptr<celix::impl::ServiceTrackingEntryFunctions>{new celix::impl::ServiceTrackingEntryFunctions()}; auto set = opts.set; if (set) { - auto voidfunc = [set](void *voidSvc) { - I *typedSvc = static_cast<I*>(voidSvc); + auto voidfunc = [entryPtr, factory, set](void *voidSvc) { + I *typedSvc = svcFor<I>(factory, entryPtr->setAdaptersCache, voidSvc); set(typedSvc); + updateSetCache(entryPtr->setAdaptersCache, voidSvc); }; - entry.functions->set = voidfunc; - entry.cOpts.set = [](void *handle, void *svc) { + entry->functions->set = voidfunc; + entry->cOpts.set = [](void *handle, void *svc) { auto *fentry = static_cast<celix::impl::ServiceTrackingEntryFunctions*>(handle); (fentry->set)(svc); }; @@ -403,12 +503,13 @@ long celix::BundleContext::trackServicesWithOptions(const celix::ServiceTracking auto setWithProperties = opts.setWithProperties; if (setWithProperties) { - auto voidfunc = [setWithProperties](void *voidSvc, const celix::Properties &props) { - I *typedSvc = static_cast<I*>(voidSvc); + auto voidfunc = [entryPtr, factory, setWithProperties](void *voidSvc, const celix::Properties &props) { + I *typedSvc = svcFor<I>(factory, entryPtr->setAdaptersCache, voidSvc); setWithProperties(typedSvc, props); + updateSetCache(entryPtr->setAdaptersCache, voidSvc); }; - entry.functions->setWithProperties = voidfunc; - entry.cOpts.setWithProperties = [](void *handle, void *svc, const celix_properties_t *c_props) { + entry->functions->setWithProperties = voidfunc; + entry->cOpts.setWithProperties = [](void *handle, void *svc, const celix_properties_t *c_props) { auto *fentry = static_cast<celix::impl::ServiceTrackingEntryFunctions*>(handle); celix::Properties props = createFromCProps(c_props); (fentry->setWithProperties)(svc, props); @@ -417,12 +518,13 @@ long celix::BundleContext::trackServicesWithOptions(const celix::ServiceTracking auto setWithOwner = opts.setWithOwner; if (setWithOwner) { - auto voidfunc = [setWithOwner](void *voidSvc, const celix::Properties &props, const celix::Bundle &bnd) { - I *typedSvc = static_cast<I*>(voidSvc); + auto voidfunc = [entryPtr, factory, setWithOwner](void *voidSvc, const celix::Properties &props, const celix::Bundle &bnd) { + I *typedSvc = svcFor<I>(factory, entryPtr->setAdaptersCache, voidSvc); setWithOwner(typedSvc, props, bnd); + updateSetCache(entryPtr->setAdaptersCache, voidSvc); }; - entry.functions->setWithOwner = voidfunc; - entry.cOpts.setWithOwner = [](void *handle, void *svc, const celix_properties_t *c_props, const celix_bundle_t *c_bnd) { + entry->functions->setWithOwner = voidfunc; + entry->cOpts.setWithOwner = [](void *handle, void *svc, const celix_properties_t *c_props, const celix_bundle_t *c_bnd) { auto *fentry = static_cast<celix::impl::ServiceTrackingEntryFunctions*>(handle); celix::Properties props = createFromCProps(c_props); auto m_bnd = const_cast<celix_bundle_t *>(c_bnd); @@ -433,12 +535,12 @@ long celix::BundleContext::trackServicesWithOptions(const celix::ServiceTracking auto add = opts.add; if (add) { - auto voidfunc = [add](void *voidSvc) { - I *typedSvc = static_cast<I*>(voidSvc); + auto voidfunc = [entryPtr, factory, add](void *voidSvc) { + I *typedSvc = svcFor<I>(factory, entryPtr->addAndRemoveAdaptersCache, voidSvc); add(typedSvc); }; - entry.functions->add = voidfunc; - entry.cOpts.add = [](void *handle, void *svc) { + entry->functions->add = voidfunc; + entry->cOpts.add = [](void *handle, void *svc) { auto *fentry = static_cast<celix::impl::ServiceTrackingEntryFunctions*>(handle); (fentry->add)(svc); }; @@ -446,26 +548,26 @@ long celix::BundleContext::trackServicesWithOptions(const celix::ServiceTracking auto addWithProperties = opts.addWithProperties; if (addWithProperties) { - auto voidfunc = [addWithProperties](void *voidSvc, const celix::Properties &props) { - I *typedSvc = static_cast<I*>(voidSvc); + auto voidfunc = [entryPtr, factory, addWithProperties](void *voidSvc, const celix::Properties &props) { + I *typedSvc = svcFor<I>(factory, entryPtr->addAndRemoveAdaptersCache, voidSvc); addWithProperties(typedSvc, props); }; - entry.functions->addWithProperties = voidfunc; - entry.cOpts.addWithProperties = [](void *handle, void *svc, const celix_properties_t *c_props) { + entry->functions->addWithProperties = voidfunc; + entry->cOpts.addWithProperties = [](void *handle, void *svc, const celix_properties_t *c_props) { auto *fentry = static_cast<celix::impl::ServiceTrackingEntryFunctions*>(handle); celix::Properties props = createFromCProps(c_props); (fentry->addWithProperties)(svc, props); }; } - auto addWithOwner = opts.setWithOwner; + auto addWithOwner = opts.addWithOwner; if (addWithOwner) { - auto voidfunc = [addWithOwner](void *voidSvc, const celix::Properties &props, const celix::Bundle &bnd) { - I *typedSvc = static_cast<I*>(voidSvc); + auto voidfunc = [entryPtr, factory, addWithOwner](void *voidSvc, const celix::Properties &props, const celix::Bundle &bnd) { + I *typedSvc = svcFor<I>(factory, entryPtr->addAndRemoveAdaptersCache, voidSvc); addWithOwner(typedSvc, props, bnd); }; - entry.functions->addWithOwner = voidfunc; - entry.cOpts.addWithOwner = [](void *handle, void *svc, const celix_properties_t *c_props, const celix_bundle_t *c_bnd) { + entry->functions->addWithOwner = voidfunc; + entry->cOpts.addWithOwner = [](void *handle, void *svc, const celix_properties_t *c_props, const celix_bundle_t *c_bnd) { auto *fentry = static_cast<celix::impl::ServiceTrackingEntryFunctions*>(handle); celix::Properties props = createFromCProps(c_props); auto m_bnd = const_cast<celix_bundle_t *>(c_bnd); @@ -476,12 +578,13 @@ long celix::BundleContext::trackServicesWithOptions(const celix::ServiceTracking auto remove = opts.remove; if (remove) { - auto voidfunc = [remove](void *voidSvc) { - I *typedSvc = static_cast<I*>(voidSvc); + auto voidfunc = [entryPtr, factory, remove](void *voidSvc) { + I *typedSvc = svcFor<I>(factory, entryPtr->addAndRemoveAdaptersCache, voidSvc); remove(typedSvc); + removeFromCache(entryPtr->addAndRemoveAdaptersCache, voidSvc); }; - entry.functions->remove = voidfunc; - entry.cOpts.remove = [](void *handle, void *svc) { + entry->functions->remove = voidfunc; + entry->cOpts.remove = [](void *handle, void *svc) { auto *fentry = static_cast<celix::impl::ServiceTrackingEntryFunctions*>(handle); (fentry->add)(svc); }; @@ -489,12 +592,13 @@ long celix::BundleContext::trackServicesWithOptions(const celix::ServiceTracking auto removeWithProperties = opts.removeWithProperties; if (removeWithProperties) { - auto voidfunc = [removeWithProperties](void *voidSvc, const celix::Properties &props) { - I *typedSvc = static_cast<I*>(voidSvc); + auto voidfunc = [entryPtr, factory, removeWithProperties](void *voidSvc, const celix::Properties &props) { + I *typedSvc = svcFor<I>(factory, entryPtr->addAndRemoveAdaptersCache, voidSvc); removeWithProperties(typedSvc, props); + removeFromCache(entryPtr->addAndRemoveAdaptersCache, voidSvc); }; - entry.functions->removeWithProperties = voidfunc; - entry.cOpts.removeWithProperties = [](void *handle, void *svc, const celix_properties_t *c_props) { + entry->functions->removeWithProperties = voidfunc; + entry->cOpts.removeWithProperties = [](void *handle, void *svc, const celix_properties_t *c_props) { auto *fentry = static_cast<celix::impl::ServiceTrackingEntryFunctions*>(handle); celix::Properties props = createFromCProps(c_props); (fentry->removeWithProperties)(svc, props); @@ -503,12 +607,13 @@ long celix::BundleContext::trackServicesWithOptions(const celix::ServiceTracking auto removeWithOwner = opts.removeWithOwner; if (removeWithOwner) { - auto voidfunc = [removeWithOwner](void *voidSvc, const celix::Properties &props, const celix::Bundle &bnd) { - I *typedSvc = static_cast<I*>(voidSvc); + auto voidfunc = [entryPtr, factory, removeWithOwner](void *voidSvc, const celix::Properties &props, const celix::Bundle &bnd) { + I *typedSvc = svcFor<I>(factory, entryPtr->addAndRemoveAdaptersCache, voidSvc); removeWithOwner(typedSvc, props, bnd); + removeFromCache(entryPtr->addAndRemoveAdaptersCache, voidSvc); }; - entry.functions->removeWithOwner = voidfunc; - entry.cOpts.removeWithOwner = [](void *handle, void *svc, const celix_properties_t *c_props, const celix_bundle_t *c_bnd) { + entry->functions->removeWithOwner = voidfunc; + entry->cOpts.removeWithOwner = [](void *handle, void *svc, const celix_properties_t *c_props, const celix_bundle_t *c_bnd) { auto *fentry = static_cast<celix::impl::ServiceTrackingEntryFunctions*>(handle); celix::Properties props = createFromCProps(c_props); auto m_bnd = const_cast<celix_bundle_t *>(c_bnd); @@ -517,14 +622,14 @@ long celix::BundleContext::trackServicesWithOptions(const celix::ServiceTracking }; } - entry.cOpts.filter.serviceName = opts.filter.serviceName.c_str(); - entry.cOpts.filter.serviceLanguage = opts.filter.serviceLanguage.c_str(); - entry.cOpts.filter.versionRange = opts.filter.versionRange.c_str(); - entry.cOpts.filter.filter = opts.filter.filter.c_str(); + entry->cOpts.filter.serviceName = opts.filter.serviceName.c_str(); + entry->cOpts.filter.serviceLanguage = opts.filter.serviceLanguage.c_str(); + entry->cOpts.filter.versionRange = opts.filter.versionRange.c_str(); + entry->cOpts.filter.filter = opts.filter.filter.c_str(); - entry.cOpts.callbackHandle = entry.functions.get(); + entry->cOpts.callbackHandle = entry->functions.get(); - return this->pimpl->trackServicesInternal(std::move(entry)); + return this->trackServicesInternal(std::move(entry)); } template<typename I> @@ -535,20 +640,71 @@ bool celix::BundleContext::useServiceWithId(long serviceId, const std::string &/ } template<typename I> +bool celix::BundleContext::useServiceWithId(long serviceId, const std::string &/*serviceName*/ /*sanity*/, const std::function<void(I &svc)> &/*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; +} + +template<typename I> bool celix::BundleContext::useService(const std::string &serviceName, const std::function<void(I &svc, const celix::Properties &props, const celix::Bundle &svcOwner)> &use) noexcept { - return this->pimpl->useServiceInternal(serviceName, [use](void *voidSvc, const celix::Properties &props, const celix::Bundle &svcOwner) { + return this->pimpl->useServiceInternal(serviceName, "", "", [use](void *voidSvc, const celix::Properties &props, const celix::Bundle &svcOwner) { I *typedSvc = static_cast<I*>(voidSvc); use(*typedSvc, props, svcOwner); }); } template<typename I> +bool celix::BundleContext::useService(const std::string &serviceName, const std::function<void(I &svc)> &use) noexcept { + return this->pimpl->useServiceInternal(serviceName, "", "", [use](void *voidSvc, const celix::Properties &, const celix::Bundle &) { + I *typedSvc = static_cast<I*>(voidSvc); + use(*typedSvc); + }); +} + +template<typename I> +bool celix::BundleContext::useService(const std::function<void(I &svc)> &use) noexcept { + using namespace celix; + I* dummy = nullptr; + auto &factory = serviceUsageAdapterFactoryFor(dummy); + bool called = this->pimpl->useServiceInternal(factory.serviceName(), factory.serviceVersionRange(), factory.serviceLanguage(), [&](void *voidSvc, const celix::Properties&, const celix::Bundle&){ + auto *adapter = factory.createAdapter(voidSvc); + auto *adapted = adapter->adapt(); + use(*adapted); + free(adapter); + }); + return called; +} + +template<typename I> +void celix::BundleContext::useServices(const std::function<void(I &svc)> &use) noexcept { + using namespace celix; + I* dummy = nullptr; + auto &factory = serviceUsageAdapterFactoryFor(dummy); + this->pimpl->useServicesInternal(factory.serviceName(), factory.serviceVersionRange(), factory.serviceLanguage(), [&](void *voidSvc, const celix::Properties&, const celix::Bundle&){ + auto *adapter = factory.createAdapter(voidSvc); + auto *adapted = adapter->adapt(); + use(*adapted); + free(adapter); + }); +} + +template<typename I> void celix::BundleContext::useServices(const std::string &serviceName, const std::function<void(I &svc, const celix::Properties &props, const celix::Bundle &svcOwner)> &use) noexcept { - this->pimpl->useServicesInternal(serviceName, [use](void *voidSvc, const celix::Properties &props, const celix::Bundle &svcOwner) { + this->pimpl->useServicesInternal(serviceName, "", "", [use](void *voidSvc, const celix::Properties &props, const celix::Bundle &svcOwner) { I *typedSvc = static_cast<I*>(voidSvc); use(*typedSvc, props, svcOwner); }); } +template<typename I> +void celix::BundleContext::useServices(const std::string &serviceName, const std::function<void(I &svc)> &use) noexcept { + this->pimpl->useServicesInternal(serviceName, "", "", [use](void *voidSvc, const celix::Properties &, const celix::Bundle &) { + I *typedSvc = static_cast<I*>(voidSvc); + use(*typedSvc); + }); +} + + #endif //CELIX_IMPL_BUNDLECONTEXTIMPL_H http://git-wip-us.apache.org/repos/asf/celix/blob/d9e95de8/libs/framework/src/service_tracker.c ---------------------------------------------------------------------- diff --git a/libs/framework/src/service_tracker.c b/libs/framework/src/service_tracker.c index e718874..c2d236f 100644 --- a/libs/framework/src/service_tracker.c +++ b/libs/framework/src/service_tracker.c @@ -606,10 +606,10 @@ celix_service_tracker_t* celix_serviceTracker_createWithOptions( //setting filter if (opts->filter.filter != NULL && opts->filter.versionRange != NULL) { //TODO version range - asprintf(&tracker->filter, "&((%s=%s)(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang, opts->filter.filter); + asprintf(&tracker->filter, "(&(%s=%s)(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang, opts->filter.filter); } else if (opts->filter.versionRange != NULL) { //TODO version range - asprintf(&tracker->filter, "&((%s=%s)(%s=%s))", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang); + asprintf(&tracker->filter, "(&(%s=%s)(%s=%s))", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang); } else if (opts->filter.filter != NULL) { asprintf(&tracker->filter, "(&(%s=%s)(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, opts->filter.serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang, opts->filter.filter); } else {
