You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2019/01/07 20:11:49 UTC
[celix] 01/22: CELIX-438: Separates ServiceRegistry & Framework
This is an automated email from the ASF dual-hosted git repository.
pnoltes pushed a commit to branch feature/cxx
in repository https://gitbox.apache.org/repos/asf/celix.git
commit e27350fa503355d43d8ca5a18734932dea26d49c
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Sun Dec 30 15:44:25 2018 +0100
CELIX-438: Separates ServiceRegistry & Framework
---
.travis.yml | 35 +-
CMakeLists.txt | 2 -
.../service/private/include/framework_patch.h | 2 +-
.../private/test/rsa_client_server_tests.cpp | 2 +-
bundles/shell/CMakeLists.txt | 2 +
{libs => bundles/shell/cxx_shell}/CMakeLists.txt | 28 +-
.../shell/cxx_shell/gtest}/CMakeLists.txt | 24 +-
.../cxx_shell/gtest/src/main.cc} | 26 +-
.../cxx_shell/include/celix/IShell.h} | 27 +-
.../shell/cxx_shell/include/celix/IShellCommand.h | 48 ++
bundles/shell/cxx_shell/src/ShellActivator.cc | 145 +++++
bundles/shell/shell_bonjour/CMakeLists.txt | 2 +-
cmake/celix_project/AddGTest.cmake | 2 +-
libs/CMakeLists.txt | 3 +
libs/framework/CMakeLists.txt | 4 +-
libs/framework/include/celix/Constants.h | 21 +-
libs/framework/src/{bundle.c => BundleImpl.c} | 0
libs/framework/src/bundle_context.c.orig | 2 +-
libs/{ => framework_cxx}/CMakeLists.txt | 23 +-
libs/{ => framework_cxx/gtest}/CMakeLists.txt | 25 +-
libs/framework_cxx/gtest/src/Framework_tests.cc | 127 ++++
.../framework_cxx/gtest/src/main.cc | 26 +-
libs/framework_cxx/include/celix/Framework.h | 88 +++
libs/framework_cxx/include/celix/IBundle.h | 63 ++
.../framework_cxx/include/celix/IBundleActivator.h | 29 +-
libs/framework_cxx/include/celix/IBundleContext.h | 137 ++++
.../framework_cxx/include/celix/api.h | 26 +-
libs/framework_cxx/src/BundleImpl.h | 178 +++++
libs/framework_cxx/src/Framework.cc | 350 ++++++++++
libs/{ => registry}/CMakeLists.txt | 23 +-
libs/{ => registry/gtest}/CMakeLists.txt | 27 +-
libs/registry/gtest/src/Filter_tests.cc | 207 ++++++
.../gtest/src/RegistryConcurrency_tests.cc | 75 +++
libs/registry/gtest/src/Registry_tests.cc | 269 ++++++++
libs/registry/gtest/src/ServiceTracking_tests.cc | 275 ++++++++
.../registry/gtest/src/main.cc | 26 +-
libs/registry/include/celix/Constants.h | 43 ++
libs/registry/include/celix/Filter.h | 73 +++
libs/registry/include/celix/IResourceBundle.h | 57 ++
.../registry/include/celix/IServiceFactory.h | 32 +-
libs/registry/include/celix/Properties.h | 60 ++
libs/registry/include/celix/ServiceRegistry.h | 509 +++++++++++++++
libs/registry/include/celix/Utils.h | 102 +++
libs/registry/src/Filter.cc | 464 +++++++++++++
libs/registry/src/ServiceRegistry.cc | 716 +++++++++++++++++++++
45 files changed, 4166 insertions(+), 239 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index ff86367..e3536d2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,3 @@
-
-sudo: required
-group: deprecated-2017Q2
-
-language: c
-
env:
global:
- COVERITY_SCAN_BUILD_COMMAND="make"
@@ -19,17 +13,17 @@ env:
matrix:
include:
- os: linux
- dist: trusty
+ dist: xenial
compiler: gcc
- os: linux
- dist: trusty
+ dist: xenial
compiler: clang
- os: osx
osx_image: xcode7.3
compiler: clang
env: MACOSX_DEPLOYMENT_TARGET=10.11
- os: linux
- dist: trusty
+ dist: xenial
compiler: gcc
env: SANITIZE=1
@@ -41,27 +35,12 @@ matrix:
#services: docker
before_install:
- - if [ "$TRAVIS_OS_NAME" = "linux" ] && [ -z "$ANDROID" ]; then sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y && sudo apt-get -qq update && sudo apt-get install -y uuid-dev libxml2-dev lcov libffi-dev gcc-4.8 g++-4.8; fi
- - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update && brew install lcov libffi zeromq czmq && brew link --force libffi; fi
+ - if [ "$TRAVIS_OS_NAME" = "linux" ] sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
+ - if [ "$TRAVIS_OS_NAME" = "linux" ] sudo apt-get update -qq
+ - if [ "$TRAVIS_OS_NAME" = "linux" ] sudo apt-get install -qq uuid-dev libxml2-dev lcov libffi-dev libgoogle-glog-dev libczmq-dev libcpputest-dev libjansson-dev
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update && brew install lcov libffi zeromq czmq glog cpputest jansson && brew link --force libffi; fi
before_script:
- - wget https://github.com/cpputest/cpputest/releases/download/v3.8/cpputest-3.8.tar.gz -O /tmp/cpputest.tar.gz
- - tar -xzvf /tmp/cpputest.tar.gz -C /tmp
- - if [ "$CC" = "clang" ]; then export CXX="clang++"; fi && cd /tmp/cpputest-* && ./configure --prefix=/usr/local && make && sudo make install && cd -
- - cd /tmp/cpputest-* && ./configure --prefix=/usr/local && make && sudo make install && cd -
- - wget https://github.com/zeromq/libzmq/releases/download/v4.2.1/zeromq-4.2.1.tar.gz -O /tmp/zeromq.tar.gz
- - tar -xzvf /tmp/zeromq.tar.gz -C /tmp && cd /tmp/zeromq-* && mkdir build && cd build
- - if [ "$TRAVIS_OS_NAME" = "linux" ] && [ -z "$ANDROID" ]; then cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DENABLE_CURVE=ON .. && make && sudo make install; fi
- - cd $TRAVIS_BUILD_DIR
- - wget https://github.com/zeromq/czmq/releases/download/v4.0.2/czmq-4.0.2.tar.gz -O /tmp/czmq.tar.gz
- - tar -xzvf /tmp/czmq.tar.gz -C /tmp && cd /tmp/czmq-* && mkdir build && cd build
- - if [ "$TRAVIS_OS_NAME" = "linux" ] && [ -z "$ANDROID" ]; then cmake -DCMAKE_INSTALL_PREFIX=/usr/local .. && make && sudo make install; fi
- - cd $TRAVIS_BUILD_DIR
- - git clone https://github.com/akheron/jansson.git jansson-build
- - cd jansson-build && git checkout 2.7
- - cmake -DJANSSON_BUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr/local . && make
- - sudo make install
- - cd -
- mkdir build install
- export BUILD_OPTIONS=" \
-DBUILD_RSA_REMOTE_SERVICE_ADMIN_DFI=ON \
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 50bd853..9ba636e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -56,8 +56,6 @@ option(ENABLE_GTESTING "Enables unit testing using GTest" ON)
if (ENABLE_TESTING)
enable_testing()
-endif ()
-if (ENABLE_TESTING AND ENABLE_GTESTING)
include(cmake/celix_project/AddGTest.cmake)
endif ()
diff --git a/bundles/config_admin/service/private/include/framework_patch.h b/bundles/config_admin/service/private/include/framework_patch.h
index 98194e1..8017f51 100644
--- a/bundles/config_admin/service/private/include/framework_patch.h
+++ b/bundles/config_admin/service/private/include/framework_patch.h
@@ -31,7 +31,7 @@
/* celix.framework.public */
#include "celix_errno.h"
-#include "bundle.h"
+#include "BundleImpl.h"
#include "service_reference.h"
celix_status_t bundle_getBundleLocation(bundle_pt bundle, const char **location);
diff --git a/bundles/remote_services/remote_service_admin_shm/private/test/rsa_client_server_tests.cpp b/bundles/remote_services/remote_service_admin_shm/private/test/rsa_client_server_tests.cpp
index 13c6941..fd3dd03 100644
--- a/bundles/remote_services/remote_service_admin_shm/private/test/rsa_client_server_tests.cpp
+++ b/bundles/remote_services/remote_service_admin_shm/private/test/rsa_client_server_tests.cpp
@@ -35,7 +35,7 @@ extern "C" {
#include "framework.h"
#include "remote_service_admin.h"
#include "calculator_service.h"
- #include "bundle.h"
+ #include "BundleImpl.h"
#define DISCOVERY_CFG_NAME "apache_celix_rsa_discovery_shm"
#define RSA_HTTP_NAME "apache_celix_remote_service_admin_shm"
diff --git a/bundles/shell/CMakeLists.txt b/bundles/shell/CMakeLists.txt
index ae2d39a..2fd81dc 100644
--- a/bundles/shell/CMakeLists.txt
+++ b/bundles/shell/CMakeLists.txt
@@ -19,3 +19,5 @@ add_subdirectory(shell)
add_subdirectory(remote_shell)
add_subdirectory(shell_bonjour)
add_subdirectory(shell_tui)
+
+add_subdirectory(cxx_shell)
diff --git a/libs/CMakeLists.txt b/bundles/shell/cxx_shell/CMakeLists.txt
similarity index 56%
copy from libs/CMakeLists.txt
copy to bundles/shell/cxx_shell/CMakeLists.txt
index 5df35a2..070f05e 100644
--- a/libs/CMakeLists.txt
+++ b/bundles/shell/cxx_shell/CMakeLists.txt
@@ -15,17 +15,23 @@
# specific language governing permissions and limitations
# under the License.
-#utils, dfi and etcdlib are standalone
-#(e.g. no dependency on celix framework
-add_subdirectory(utils)
-add_subdirectory(dfi)
-add_subdirectory(etcdlib)
+find_package(glog REQUIRED)
-add_subdirectory(framework)
+#TODO rename to celix::shell && celix::shell_api
-#launcher
-add_subdirectory(launcher)
+add_library(shell_api_cxx INTERFACE)
+target_include_directories(shell_api INTERFACE
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
+ $<INSTALL_INTERFACE:include/celix/shell>
+)
-#add_subdirectory(event_admin)# event_admin is unstable
-add_subdirectory(dependency_manager)
-add_subdirectory(dependency_manager_cxx)
\ No newline at end of file
+add_library(celix_shell_cxx STATIC
+ src/ShellActivator.cc
+ include/celix/IShell.h)
+target_include_directories(celix_shell_cxx PRIVATE src)
+target_include_directories(celix_shell_cxx PUBLIC include)
+target_link_libraries(celix_shell_cxx PRIVATE glog::glog celix_framework_cxx)
+
+#if (ENABLE_TESTING)
+# add_subdirectory(gtest)
+#endif ()
\ No newline at end of file
diff --git a/libs/CMakeLists.txt b/bundles/shell/cxx_shell/gtest/CMakeLists.txt
similarity index 66%
copy from libs/CMakeLists.txt
copy to bundles/shell/cxx_shell/gtest/CMakeLists.txt
index 5df35a2..b4a7204 100644
--- a/libs/CMakeLists.txt
+++ b/bundles/shell/cxx_shell/gtest/CMakeLists.txt
@@ -5,9 +5,9 @@
# 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
@@ -15,17 +15,11 @@
# specific language governing permissions and limitations
# under the License.
-#utils, dfi and etcdlib are standalone
-#(e.g. no dependency on celix framework
-add_subdirectory(utils)
-add_subdirectory(dfi)
-add_subdirectory(etcdlib)
+set(SOURCES
+ src/main.cc
+)
+add_executable(celix_shell_cxx_tests ${SOURCES})
+target_link_libraries(celix_shell_cxx_tests PRIVATE gtest celix_framework_cxx celix_shell_cxx)
-add_subdirectory(framework)
-
-#launcher
-add_subdirectory(launcher)
-
-#add_subdirectory(event_admin)# event_admin is unstable
-add_subdirectory(dependency_manager)
-add_subdirectory(dependency_manager_cxx)
\ No newline at end of file
+add_test(NAME celix_shell_cxx_tests COMMAND celix_shell_cxx_tests)
+SETUP_TARGET_FOR_COVERAGE(celix_shell_cxx_tests_cov celix_shell_cxx_tests ${CMAKE_BINARY_DIR}/coverage/celix_shell_cxx_tests/celix_shell_cxx_tests)
\ No newline at end of file
diff --git a/bundles/config_admin/service/private/include/framework_patch.h b/bundles/shell/cxx_shell/gtest/src/main.cc
similarity index 63%
copy from bundles/config_admin/service/private/include/framework_patch.h
copy to bundles/shell/cxx_shell/gtest/src/main.cc
index 98194e1..a76daa7 100644
--- a/bundles/config_admin/service/private/include/framework_patch.h
+++ b/bundles/shell/cxx_shell/gtest/src/main.cc
@@ -16,24 +16,18 @@
*specific language governing permissions and limitations
*under the License.
*/
-/*
- * framework_patch.h
- *
- * \date Aug 12, 2013
- * \author <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
- * \copyright Apache License, Version 2.0
- */
-
-#ifndef BUNDLE_PATCH_H_
-#define BUNDLE_PATCH_H_
+#include <gtest/gtest.h>
+#include <glog/logging.h>
+int main(int argc, char **argv) {
+ google::InitGoogleLogging(argv[0]);
+ google::LogToStderr();
-/* celix.framework.public */
-#include "celix_errno.h"
-#include "bundle.h"
-#include "service_reference.h"
+ ::testing::InitGoogleTest(&argc, argv);
+ int rc = RUN_ALL_TESTS();
-celix_status_t bundle_getBundleLocation(bundle_pt bundle, const char **location);
+ google::ShutdownGoogleLogging();
-#endif /* BUNDLE_PATCH_H_ */
+ return rc;
+}
\ No newline at end of file
diff --git a/bundles/config_admin/service/private/include/framework_patch.h b/bundles/shell/cxx_shell/include/celix/IShell.h
similarity index 64%
copy from bundles/config_admin/service/private/include/framework_patch.h
copy to bundles/shell/cxx_shell/include/celix/IShell.h
index 98194e1..9809ac5 100644
--- a/bundles/config_admin/service/private/include/framework_patch.h
+++ b/bundles/shell/cxx_shell/include/celix/IShell.h
@@ -16,24 +16,21 @@
*specific language governing permissions and limitations
*under the License.
*/
-/*
- * framework_patch.h
- *
- * \date Aug 12, 2013
- * \author <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
- * \copyright Apache License, Version 2.0
- */
+#ifndef CXX_CELIX_ISHELL_H
+#define CXX_CELIX_ISHELL_H
-#ifndef BUNDLE_PATCH_H_
-#define BUNDLE_PATCH_H_
+#include <iostream>
+namespace celix {
+ class IShell {
+ public:
+ static constexpr const char * const SERVICE_FQN = "celix::IShell [Version 1]";
-/* celix.framework.public */
-#include "celix_errno.h"
-#include "bundle.h"
-#include "service_reference.h"
+ virtual ~IShell() = default;
-celix_status_t bundle_getBundleLocation(bundle_pt bundle, const char **location);
+ virtual bool executeCommand(const std::string &commandLine, std::ostream &out, std::ostream &err) noexcept = 0;
+ };
+}
-#endif /* BUNDLE_PATCH_H_ */
+#endif //CXX_CELIX_ISHELL_H
diff --git a/bundles/shell/cxx_shell/include/celix/IShellCommand.h b/bundles/shell/cxx_shell/include/celix/IShellCommand.h
new file mode 100644
index 0000000..8a30133
--- /dev/null
+++ b/bundles/shell/cxx_shell/include/celix/IShellCommand.h
@@ -0,0 +1,48 @@
+/**
+ *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>
+
+#ifndef CXX_CELIX_ISHELLCOMMAND_H
+#define CXX_CELIX_ISHELLCOMMAND_H
+
+namespace celix {
+
+ class IShellCommand {
+ public:
+ static constexpr const char * const SERVICE_FQN = "celix::IShellCommand [Version 1]";
+
+ static constexpr const char * const COMMAND_NAME = "name";
+ static constexpr const char * const COMMAND_USAGE = "usage";
+ static constexpr const char * const COMMAND_DESCRIPTION = "description";
+
+ virtual ~IShellCommand() = default;
+
+ virtual void executeCommand(const std::string &commandLine, std::ostream &out, std::ostream &err) noexcept = 0;
+ };
+
+ using ShellCommandFunction = std::function<void(const std::string &commandLine, std::ostream &out, std::ostream &err)>;
+ static constexpr const char * const SHELL_COMMAND_FUNCTION_SERVICE_FQN = "celix::ShellFunction [Version 1]";
+ static constexpr const char * const SHELL_COMMAND_FUNCTION_COMMAND_NAME = "name";
+ static constexpr const char * const SHELL_COMMAND_FUNCTION_COMMAND_USAGE = "usage";
+ static constexpr const char * const SHELL_COMMAND_FUNCTION_COMMAND_DESCRIPTION = "description";
+}
+
+#endif //CXX_CELIX_ISHELLCOMMAND_H
diff --git a/bundles/shell/cxx_shell/src/ShellActivator.cc b/bundles/shell/cxx_shell/src/ShellActivator.cc
new file mode 100644
index 0000000..5fd4f1f
--- /dev/null
+++ b/bundles/shell/cxx_shell/src/ShellActivator.cc
@@ -0,0 +1,145 @@
+/**
+ *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 "celix/api.h"
+#include "celix/IShellCommand.h"
+#include "celix/IShell.h"
+
+namespace {
+
+ class LbCommand : public celix::IShellCommand {
+ public:
+ LbCommand(std::shared_ptr<celix::IBundleContext> _ctx) : ctx{std::move(_ctx)} {}
+ void executeCommand(const std::string &/*command line*/, std::ostream &out, std::ostream &) noexcept override {
+ //TODO parse commandLine
+ out << "Bundles: " << std::endl;
+ ctx->useBundles([&out](const celix::IBundle &bnd) {
+ out << "|- " << bnd.id() << ": " << bnd.name() << std::endl;
+ }, true);
+ }
+ private:
+ std::shared_ptr<celix::IBundleContext> ctx;
+ };
+
+ celix::ServiceRegistration registerLb(std::shared_ptr<celix::IBundleContext> ctx) {
+ celix::Properties props{};
+ props[celix::IShellCommand::COMMAND_NAME] = "lb";
+ props[celix::IShellCommand::COMMAND_USAGE] = "list installed bundles";
+ props[celix::IShellCommand::COMMAND_DESCRIPTION] = "TODO";
+ return ctx->registerService(std::shared_ptr<celix::IShellCommand>{new LbCommand{ctx}}, std::move(props));
+ }
+
+ celix::ServiceRegistration registerHelp(std::shared_ptr<celix::IBundleContext> ctx) {
+
+ celix::ShellCommandFunction help = [ctx](const std::string &, std::ostream &out, std::ostream &) {
+
+ std::string hasCommandNameFilter = std::string{"("} + celix::IShellCommand::COMMAND_NAME + "=*)";
+ //TODO parse command line to see if details is requested instead of overview
+ std::vector<std::string> commands{};
+ ctx->useServices<celix::IShellCommand>([&](celix::IShellCommand&, const celix::Properties &props) {
+ commands.push_back(celix::getProperty(props, celix::IShellCommand::COMMAND_NAME, "!Error!"));
+ }, hasCommandNameFilter);
+
+ hasCommandNameFilter = std::string{"("} + celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME + "=*)";
+
+ std::function<void(celix::ShellCommandFunction&, const celix::Properties&)> use = [&](celix::ShellCommandFunction&, const celix::Properties &props) {
+ commands.push_back(celix::getProperty(props, celix::IShellCommand::COMMAND_NAME, "!Error!"));
+ };
+ ctx->useFunctionServices(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, use, hasCommandNameFilter);
+
+ //TODO useCService with a shell command service struct
+
+ out << "Available commands: " << std::endl;
+ for (auto &name : commands) {
+ out << "|- " << name << std::endl;
+ }
+ };
+
+ celix::Properties props{};
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_NAME] = "help";
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_USAGE] = "help [command name]";
+ props[celix::SHELL_COMMAND_FUNCTION_COMMAND_DESCRIPTION] = "TODO";
+ return ctx->registerFunctionService(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, std::move(help), std::move(props));
+ }
+
+ class Shell : public celix::IShell {
+ public:
+ Shell(std::shared_ptr<celix::IBundleContext> _ctx) : ctx{std::move(_ctx)} {
+ celix::ServiceTrackerOptions<celix::IShellCommand> opts1{};
+ opts1.updateWithProperties = [this](std::vector<std::tuple<celix::IShellCommand*,const celix::Properties*>> services) {
+ std::lock_guard<std::mutex> lck(commands.mutex);
+ commands.commands = std::move(services);
+ };
+ trk1 = ctx->trackServices(opts1);
+
+ celix::ServiceTrackerOptions<celix::ShellCommandFunction> opts2{};
+ opts2.updateWithProperties = [this](std::vector<std::tuple<celix::ShellCommandFunction*,const celix::Properties*>> services) {
+ std::lock_guard<std::mutex> lck(commands.mutex);
+ commands.commandFunctions = std::move(services);
+ };
+ trk2 = ctx->trackFunctionServices(celix::SHELL_COMMAND_FUNCTION_SERVICE_FQN, opts2);
+ }
+
+ bool executeCommand(const std::string &commandLine, std::ostream &out, std::ostream &) noexcept override {
+ out << "TODO call command '" << commandLine << "'" << std::endl;
+ return false;
+ }
+ private:
+ std::shared_ptr<celix::IBundleContext> ctx;
+
+ celix::ServiceTracker trk1{};
+ celix::ServiceTracker trk2{};
+
+ struct {
+ mutable std::mutex mutex{};
+ std::vector<std::tuple<celix::IShellCommand*, const celix::Properties*>> commands;
+ std::vector<std::tuple<celix::ShellCommandFunction*, const celix::Properties*>> commandFunctions;
+ } commands{};
+ };
+
+ class ShellBundleActivator : public celix::IBundleActivator {
+ public:
+ bool start(std::shared_ptr<celix::IBundleContext> ctx) noexcept override {
+ //TODO ensure fixed framework thread that call bundle activators
+ registrations.push_back(registerLb(ctx));
+ registrations.push_back(registerHelp(ctx));
+
+ registrations.push_back(ctx->registerService(std::shared_ptr<celix::IShell>{new Shell{ctx}}));
+
+ return true;
+ }
+
+ bool stop(std::shared_ptr<celix::IBundleContext>) noexcept override {
+ registrations.clear();
+ return true;
+ }
+ private:
+ std::vector<celix::ServiceRegistration> registrations;
+ };
+}
+
+__attribute__((constructor))
+static void registerShellBundle() {
+ celix::StaticBundleOptions opts{};
+ opts.bundleActivatorFactory = [](){
+ return new ShellBundleActivator{};
+ };
+ opts.manifest[celix::MANIFEST_BUNDLE_VERSION] = "1.0.0";
+ celix::registerStaticBundle("celix::Shell", opts);
+}
\ No newline at end of file
diff --git a/bundles/shell/shell_bonjour/CMakeLists.txt b/bundles/shell/shell_bonjour/CMakeLists.txt
index 771ccdb..2f40c7e 100644
--- a/bundles/shell/shell_bonjour/CMakeLists.txt
+++ b/bundles/shell/shell_bonjour/CMakeLists.txt
@@ -33,7 +33,7 @@ if (SHELL_BONJOUR)
private/src/activator.c
private/src/bonjour_shell.c
)
- add_library(Celix::bonjour_shell ALIAS bonjour_shell)
+ add_library(Celix::bonjour_shell ALIAS bonjour_shell ../cxx_shell/src/ShellActivator.cc)
target_include_directories(bonjour_shell PRIVATE
diff --git a/cmake/celix_project/AddGTest.cmake b/cmake/celix_project/AddGTest.cmake
index 2b91c54..a1753a8 100644
--- a/cmake/celix_project/AddGTest.cmake
+++ b/cmake/celix_project/AddGTest.cmake
@@ -19,7 +19,7 @@ include(ExternalProject)
ExternalProject_Add(
googletest_project
GIT_REPOSITORY https://github.com/google/googletest.git
- GIT_TAG master
+ GIT_TAG release-1.8.1
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/gtest
INSTALL_COMMAND ""
)
diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt
index 5df35a2..88d1928 100644
--- a/libs/CMakeLists.txt
+++ b/libs/CMakeLists.txt
@@ -23,6 +23,9 @@ add_subdirectory(etcdlib)
add_subdirectory(framework)
+add_subdirectory(registry)
+add_subdirectory(framework_cxx)
+
#launcher
add_subdirectory(launcher)
diff --git a/libs/framework/CMakeLists.txt b/libs/framework/CMakeLists.txt
index 75aab2d..ecfc795 100644
--- a/libs/framework/CMakeLists.txt
+++ b/libs/framework/CMakeLists.txt
@@ -24,7 +24,7 @@ if(WIN32)
endif(WIN32)
set(SOURCES
- src/attribute.c src/bundle.c src/bundle_archive.c src/bundle_cache.c
+ src/attribute.c src/BundleImpl.c src/bundle_archive.c src/bundle_cache.c
src/bundle_context.c src/bundle_revision.c src/capability.c src/celix_errorcodes.c
src/framework.c src/manifest.c src/ioapi.c
src/manifest_parser.c src/miniunz.c src/module.c
@@ -148,7 +148,7 @@ if (ENABLE_TESTING AND FRAMEWORK_TESTS)
private/mock/bundle_revision_mock.c
private/mock/resolver_mock.c
private/mock/version_mock.c
- src/bundle.c
+ src/BundleImpl.c
src/celix_errorcodes.c
private/mock/celix_log_mock.c)
target_link_libraries(bundle_test ${CPPUTEST_LIBRARY} ${CPPUTEST_EXT_LIBRARY} Celix::utils pthread)
diff --git a/libs/framework/include/celix/Constants.h b/libs/framework/include/celix/Constants.h
index 6788b7e..adcb6a3 100644
--- a/libs/framework/include/celix/Constants.h
+++ b/libs/framework/include/celix/Constants.h
@@ -21,24 +21,19 @@
#ifndef CXX_CELIX_CONSTANTS_H
#define CXX_CELIX_CONSTANTS_H
-#include "celix_constants.h"
-
namespace celix {
class Constants {
public:
- static constexpr const char *const SERVICE_NAME = OSGI_FRAMEWORK_OBJECTCLASS;
- static constexpr const char *const SERVICE_ID = OSGI_FRAMEWORK_SERVICE_ID;
- static constexpr const char *const SERVICE_PID = OSGI_FRAMEWORK_SERVICE_PID;
- static constexpr const char *const SERVICE_RANKING = OSGI_FRAMEWORK_SERVICE_RANKING;
+ //NOTE manually aligned with celix_constants.h
+ static constexpr const char *const SERVICE_NAME = "objectClass"; //TODO rename to service.name
+ static constexpr const char *const SERVICE_LANGUAGE = "service.version";
+ static constexpr const char *const SERVICE_ID = "service.id";
+ static constexpr const char *const SERVICE_RANKING = "service.ranking";
- static constexpr const char *const SERVICE_VERSION = CELIX_FRAMEWORK_SERVICE_VERSION;
- static constexpr const char *const SERVICE_LANGUAGE = CELIX_FRAMEWORK_SERVICE_LANGUAGE;
- static constexpr const char *const SERVICE_C_LANG = CELIX_FRAMEWORK_SERVICE_C_LANGUAGE;
- static constexpr const char *const SERVICE_CXX_LANG = CELIX_FRAMEWORK_SERVICE_CXX_LANGUAGE;
+ static constexpr const char *const SERVICE_C_LANG = "C";
+ static constexpr const char *const SERVICE_CXX_LANG = "C++";
- static constexpr const char *const FRAMEWORK_STORAGE = OSGI_FRAMEWORK_FRAMEWORK_STORAGE;
- static constexpr const char *const FRAMEWORK_CLEAN = OSGI_FRAMEWORK_FRAMEWORK_STORAGE_CLEAN_NAME;
- static constexpr const char *const FRAMEWORK_UUID = OSGI_FRAMEWORK_FRAMEWORK_UUID;
+ static constexpr const char *const FRAMEWORK_UUID = "framework.uuid";
};
}
diff --git a/libs/framework/src/bundle.c b/libs/framework/src/BundleImpl.c
similarity index 100%
rename from libs/framework/src/bundle.c
rename to libs/framework/src/BundleImpl.c
diff --git a/libs/framework/src/bundle_context.c.orig b/libs/framework/src/bundle_context.c.orig
index 8d3a0cc..8866472 100644
--- a/libs/framework/src/bundle_context.c.orig
+++ b/libs/framework/src/bundle_context.c.orig
@@ -26,7 +26,7 @@
#include "constants.h"
#include "bundle_context_private.h"
#include "framework_private.h"
-#include "bundle.h"
+#include "BundleImpl.h"
#include "celix_bundle.h"
#include "celix_log.h"
#include "service_tracker.h"
diff --git a/libs/CMakeLists.txt b/libs/framework_cxx/CMakeLists.txt
similarity index 65%
copy from libs/CMakeLists.txt
copy to libs/framework_cxx/CMakeLists.txt
index 5df35a2..e5fbe8a 100644
--- a/libs/CMakeLists.txt
+++ b/libs/framework_cxx/CMakeLists.txt
@@ -15,17 +15,16 @@
# specific language governing permissions and limitations
# under the License.
-#utils, dfi and etcdlib are standalone
-#(e.g. no dependency on celix framework
-add_subdirectory(utils)
-add_subdirectory(dfi)
-add_subdirectory(etcdlib)
+find_package(glog REQUIRED)
-add_subdirectory(framework)
+#TODO rename to celix::framework
+add_library(celix_framework_cxx SHARED
+ src/Framework.cc
+)
+target_include_directories(celix_framework_cxx PRIVATE src)
+target_include_directories(celix_framework_cxx PUBLIC include)
+target_link_libraries(celix_framework_cxx PUBLIC celix::registry glog::glog) #TODO glog private and static lib
-#launcher
-add_subdirectory(launcher)
-
-#add_subdirectory(event_admin)# event_admin is unstable
-add_subdirectory(dependency_manager)
-add_subdirectory(dependency_manager_cxx)
\ No newline at end of file
+if (ENABLE_TESTING)
+ add_subdirectory(gtest)
+endif ()
\ No newline at end of file
diff --git a/libs/CMakeLists.txt b/libs/framework_cxx/gtest/CMakeLists.txt
similarity index 64%
copy from libs/CMakeLists.txt
copy to libs/framework_cxx/gtest/CMakeLists.txt
index 5df35a2..0f407cc 100644
--- a/libs/CMakeLists.txt
+++ b/libs/framework_cxx/gtest/CMakeLists.txt
@@ -5,9 +5,9 @@
# 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
@@ -15,17 +15,12 @@
# specific language governing permissions and limitations
# under the License.
-#utils, dfi and etcdlib are standalone
-#(e.g. no dependency on celix framework
-add_subdirectory(utils)
-add_subdirectory(dfi)
-add_subdirectory(etcdlib)
+set(SOURCES
+ src/main.cc
+ src/Framework_tests.cc
+)
+add_executable(celix_framework_cxx_tests ${SOURCES})
+target_link_libraries(celix_framework_cxx_tests PRIVATE gtest celix_framework_cxx)
-add_subdirectory(framework)
-
-#launcher
-add_subdirectory(launcher)
-
-#add_subdirectory(event_admin)# event_admin is unstable
-add_subdirectory(dependency_manager)
-add_subdirectory(dependency_manager_cxx)
\ No newline at end of file
+add_test(NAME celix_framework_cxx_tests COMMAND celix_framework_cxx_tests)
+SETUP_TARGET_FOR_COVERAGE(celix_framework_cxx_tests_cov celix_framework_cxx_tests ${CMAKE_BINARY_DIR}/coverage/framework/celix_framework_cxx_tests)
\ No newline at end of file
diff --git a/libs/framework_cxx/gtest/src/Framework_tests.cc b/libs/framework_cxx/gtest/src/Framework_tests.cc
new file mode 100644
index 0000000..34cad54
--- /dev/null
+++ b/libs/framework_cxx/gtest/src/Framework_tests.cc
@@ -0,0 +1,127 @@
+/**
+ *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/Framework.h"
+
+class FrameworkTest : public ::testing::Test {
+public:
+ FrameworkTest() {}
+ ~FrameworkTest(){}
+
+ celix::Framework& framework() { return fw; }
+private:
+ celix::Framework fw{};
+};
+
+
+TEST_F(FrameworkTest, CreateDestroy) {
+ //no bundles installed bundle (framework bundle)
+ EXPECT_EQ(1, framework().listBundles(true).size());
+ EXPECT_EQ(0, framework().listBundles(false).size());
+
+ bool isFramework = false;
+ framework().useBundle(0L, [&](const celix::IBundle &bnd) {
+ isFramework = bnd.isFrameworkBundle();
+ });
+ EXPECT_TRUE(isFramework);
+}
+
+TEST_F(FrameworkTest, InstallBundle) {
+
+ class EmbeddedActivator : public celix::IBundleActivator {
+ public:
+ virtual ~EmbeddedActivator() = default;
+
+ bool resolve(std::shared_ptr<celix::IBundleContext> ctx) noexcept override {
+ EXPECT_GE(ctx->bundle()->id(), 1);
+ resolveCalled = true;
+ return true;
+ }
+
+ bool start(std::shared_ptr<celix::IBundleContext>) noexcept override {
+ startCalled = true;
+ return true;
+ }
+
+ bool stop(std::shared_ptr<celix::IBundleContext>) noexcept override {
+ stopCalled = true;
+ return true;
+ }
+
+ bool resolveCalled = false;
+ bool startCalled = false;
+ bool stopCalled = false;
+ };
+
+ long bndId1 = framework().installBundle<EmbeddedActivator>("embedded");
+ EXPECT_GE(bndId1, 0);
+
+ std::shared_ptr<EmbeddedActivator> act{new EmbeddedActivator};
+ long bndId2 = framework().installBundle("embedded2", act);
+ EXPECT_GE(bndId2, 0);
+ EXPECT_NE(bndId1, bndId2);
+ EXPECT_TRUE(act->resolveCalled);
+ EXPECT_TRUE(act->startCalled);
+ EXPECT_FALSE(act->stopCalled);
+
+ framework().stopBundle(bndId2);
+ EXPECT_TRUE(act->stopCalled);
+
+ std::shared_ptr<EmbeddedActivator> act3{new EmbeddedActivator};
+ {
+ celix::Framework fw{};
+ fw.installBundle("embedded3", act3);
+ EXPECT_TRUE(act3->resolveCalled);
+ EXPECT_TRUE(act3->startCalled);
+ EXPECT_FALSE(act3->stopCalled);
+
+ //NOTE fw out of scope -> bundle stopped
+ }
+ EXPECT_TRUE(act3->stopCalled);
+}
+
+TEST_F(FrameworkTest, StaticBundleTest) {
+ class EmbeddedActivator : public celix::IBundleActivator {
+ public:
+ virtual ~EmbeddedActivator() = default;
+
+ bool start(std::shared_ptr<celix::IBundleContext>) noexcept override {
+ return true;
+ }
+ };
+
+ int count = 0;
+ auto factory = [&]() -> celix::IBundleActivator * {
+ count++;
+ return new EmbeddedActivator{};
+ };
+
+ EXPECT_EQ(0, framework().listBundles().size()); //no bundles installed;
+ celix::StaticBundleOptions opts;
+ opts.bundleActivatorFactory = std::move(factory);
+ celix::registerStaticBundle("static", opts);
+ EXPECT_EQ(1, framework().listBundles().size()); //static bundle instance installed
+ EXPECT_EQ(1, count);
+
+ celix::Framework fw{};
+ EXPECT_EQ(1, framework().listBundles().size()); //already registered static bundle instance installed.
+ EXPECT_EQ(2, count);
+}
\ No newline at end of file
diff --git a/bundles/config_admin/service/private/include/framework_patch.h b/libs/framework_cxx/gtest/src/main.cc
similarity index 63%
copy from bundles/config_admin/service/private/include/framework_patch.h
copy to libs/framework_cxx/gtest/src/main.cc
index 98194e1..a76daa7 100644
--- a/bundles/config_admin/service/private/include/framework_patch.h
+++ b/libs/framework_cxx/gtest/src/main.cc
@@ -16,24 +16,18 @@
*specific language governing permissions and limitations
*under the License.
*/
-/*
- * framework_patch.h
- *
- * \date Aug 12, 2013
- * \author <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
- * \copyright Apache License, Version 2.0
- */
-
-#ifndef BUNDLE_PATCH_H_
-#define BUNDLE_PATCH_H_
+#include <gtest/gtest.h>
+#include <glog/logging.h>
+int main(int argc, char **argv) {
+ google::InitGoogleLogging(argv[0]);
+ google::LogToStderr();
-/* celix.framework.public */
-#include "celix_errno.h"
-#include "bundle.h"
-#include "service_reference.h"
+ ::testing::InitGoogleTest(&argc, argv);
+ int rc = RUN_ALL_TESTS();
-celix_status_t bundle_getBundleLocation(bundle_pt bundle, const char **location);
+ google::ShutdownGoogleLogging();
-#endif /* BUNDLE_PATCH_H_ */
+ return rc;
+}
\ No newline at end of file
diff --git a/libs/framework_cxx/include/celix/Framework.h b/libs/framework_cxx/include/celix/Framework.h
new file mode 100644
index 0000000..60b6fca
--- /dev/null
+++ b/libs/framework_cxx/include/celix/Framework.h
@@ -0,0 +1,88 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#ifndef CXX_CELIX_FRAMEWORK_H
+#define CXX_CELIX_FRAMEWORK_H
+
+#include <memory>
+
+#include "celix/Constants.h"
+#include "celix/ServiceRegistry.h"
+#include "celix/IBundleContext.h"
+#include "celix/IBundle.h"
+#include "celix/IBundleActivator.h"
+
+namespace celix {
+
+ struct StaticBundleOptions {
+ std::string name{};
+ std::string group{};
+ std::string version{};
+ celix::Properties manifest{};
+
+ std::function<celix::IBundleActivator*()> bundleActivatorFactory{};
+
+ //TODO resources. poiting to bundle specific symbols which is linked zip file
+ char * const resoucreZip = nullptr;
+ size_t resourceZipLength = 0;
+ };
+
+ void registerStaticBundle(std::string symbolicName, const StaticBundleOptions &opts);
+ //TODO useFrameworks with a callback with as argument a fw ref
+
+ class Framework {
+ public:
+ Framework();
+ ~Framework();
+ Framework(Framework &&rhs);
+ Framework& operator=(Framework&& rhs);
+
+ Framework(const Framework& rhs) = delete;
+ Framework& operator=(const Framework &rhs) = delete;
+
+
+ template<typename T>
+ long installBundle(std::string symbolicName, celix::Properties manifest = {}, bool autoStart = true) {
+ std::shared_ptr<celix::IBundleActivator> activator{new T{}};
+ return installBundle(std::move(symbolicName), std::move(activator), std::move(manifest), autoStart);
+ }
+
+ long installBundle(std::string symbolicName, std::shared_ptr<celix::IBundleActivator> activator, celix::Properties manifest = {}, bool autoStart = true);
+
+
+ //long installBundle(const std::string &path);
+ bool startBundle(long bndId);
+ bool stopBundle(long bndId);
+ bool uninstallBundle(long bndId);
+
+ bool useBundle(long bndId, std::function<void(const celix::IBundle &bnd)> use) const;
+ int useBundles(std::function<void(const celix::IBundle &bnd)> use, bool includeFrameworkBundle = false) const;
+
+ //long bundleIdForName(const std::string &bndName) const;
+ std::vector<long> listBundles(bool includeFrameworkBundle = false) const;
+
+ celix::ServiceRegistry& registry(const std::string &lang);
+ private:
+ class Impl;
+ std::unique_ptr<Impl> pimpl;
+ };
+
+};
+
+#endif //CXX_CELIX_FRAMEWORK_H
diff --git a/libs/framework_cxx/include/celix/IBundle.h b/libs/framework_cxx/include/celix/IBundle.h
new file mode 100644
index 0000000..a8d0de1
--- /dev/null
+++ b/libs/framework_cxx/include/celix/IBundle.h
@@ -0,0 +1,63 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#ifndef CXX_CELIX_IBUNDLE_H
+#define CXX_CELIX_IBUNDLE_H
+
+#include "celix/IResourceBundle.h"
+#include "celix/Properties.h"
+
+namespace celix {
+
+ enum class BundleState {
+ INSTALLED,
+ RESOLVED,
+ ACTIVE,
+ };
+
+ class Framework; //forward declaration
+
+ class IBundle : public celix::IResourceBundle {
+ public:
+ virtual ~IBundle() = default;
+
+ virtual const std::string& name() const noexcept = 0;
+
+ virtual const std::string& symbolicName() const noexcept = 0;
+
+ virtual const std::string& group() const noexcept = 0;
+
+ virtual bool isFrameworkBundle() const noexcept = 0;
+
+ virtual void* handle() const noexcept = 0;
+
+ virtual celix::BundleState state() const noexcept = 0;
+
+ virtual const std::string& version() const noexcept = 0;
+
+ virtual const celix::Properties& manifest() const noexcept = 0;
+
+ virtual bool isValid() const noexcept = 0;
+
+ virtual celix::Framework& framework() const noexcept = 0;
+ };
+
+}
+
+#endif //CXX_CELIX_IBUNDLE_H
diff --git a/bundles/config_admin/service/private/include/framework_patch.h b/libs/framework_cxx/include/celix/IBundleActivator.h
similarity index 58%
copy from bundles/config_admin/service/private/include/framework_patch.h
copy to libs/framework_cxx/include/celix/IBundleActivator.h
index 98194e1..1196e99 100644
--- a/bundles/config_admin/service/private/include/framework_patch.h
+++ b/libs/framework_cxx/include/celix/IBundleActivator.h
@@ -16,24 +16,23 @@
*specific language governing permissions and limitations
*under the License.
*/
-/*
- * framework_patch.h
- *
- * \date Aug 12, 2013
- * \author <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
- * \copyright Apache License, Version 2.0
- */
+#ifndef CXX_CELIX_IBUNDLEACTIVATOR_H
+#define CXX_CELIX_IBUNDLEACTIVATOR_H
-#ifndef BUNDLE_PATCH_H_
-#define BUNDLE_PATCH_H_
+#include <memory>
+#include "IBundleContext.h"
-/* celix.framework.public */
-#include "celix_errno.h"
-#include "bundle.h"
-#include "service_reference.h"
+namespace celix {
+ class IBundleActivator {
+ public:
+ virtual ~IBundleActivator() = default;
-celix_status_t bundle_getBundleLocation(bundle_pt bundle, const char **location);
+ virtual bool resolve(std::shared_ptr<celix::IBundleContext> /*ctx*/) noexcept { return true; };
+ virtual bool start(std::shared_ptr<celix::IBundleContext> ctx) noexcept = 0;
+ virtual bool stop(std::shared_ptr<celix::IBundleContext> /*ctx*/) noexcept { return true; }
+ };
+}
-#endif /* BUNDLE_PATCH_H_ */
+#endif //CXX_CELIX_IBUNDLEACTIVATOR_H
diff --git a/libs/framework_cxx/include/celix/IBundleContext.h b/libs/framework_cxx/include/celix/IBundleContext.h
new file mode 100644
index 0000000..e77623a
--- /dev/null
+++ b/libs/framework_cxx/include/celix/IBundleContext.h
@@ -0,0 +1,137 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#ifndef CXX_CELIX_IBUNDLECONTEXT_H
+#define CXX_CELIX_IBUNDLECONTEXT_H
+
+#include "celix/IBundle.h"
+
+namespace celix {
+
+ //TODO rename and drop I, because it not a complete interface (templates)
+ class IBundleContext {
+ public:
+ virtual ~IBundleContext() = default;
+
+ virtual std::shared_ptr<celix::IBundle> bundle() const noexcept = 0;
+
+ template<typename I>
+ celix::ServiceRegistration registerService(I &svc, celix::Properties props = {}) {
+ return registry().registerService<I>(svc, std::move(props), bundle());
+ }
+
+ template<typename I>
+ celix::ServiceRegistration registerService(std::shared_ptr<I> svc, celix::Properties props = {}) {
+ return registry().registerService<I>(svc, std::move(props), bundle());
+ }
+
+ template<typename F>
+ celix::ServiceRegistration registerFunctionService(std::string functionName, F&& function, celix::Properties props = {}) {
+ return registry().registerFunctionService(std::move(functionName), std::forward<F>(function), std::move(props), bundle());
+ }
+
+ //TODO register C services
+
+ virtual bool useBundle(long bndId, std::function<void(const celix::IBundle &bnd)> use) const noexcept = 0;
+ virtual int useBundles(std::function<void(const celix::IBundle &bnd)> use, bool includeFrameworkBundle = true) const noexcept = 0;
+
+ template<typename I>
+ bool useService(std::function<void(I &svc)> use, const std::string &filter = "") const noexcept {
+ return registry().useService<I>(std::move(use), filter, bundle());
+ }
+
+ template<typename I>
+ bool useService(std::function<void(I &svc, const celix::Properties &props)> use, const std::string &filter = "") const noexcept {
+ return registry().useService<I>(std::move(use), filter, bundle());
+ }
+
+ template<typename I>
+ bool useService(std::function<void(I &svc, const celix::Properties &props, const celix::IResourceBundle &owner)> use, const std::string &filter = "") const noexcept {
+ return registry().useService<I>(std::move(use), filter, bundle());
+ }
+
+ template<typename F>
+ bool useFunctionService(const std::string &functionName, std::function<void(F &function)> use, const std::string &filter = "") const noexcept {
+ return registry().useFunctionService<F>(functionName, std::move(use), filter, bundle());
+ }
+
+ template<typename F>
+ bool useFunctionService(const std::string &functionName, std::function<void(F &function, const celix::Properties &props)> use, const std::string &filter = "") const noexcept {
+ return registry().useFunctionService<F>(functionName, std::move(use), filter, bundle());
+ }
+
+ template<typename F>
+ bool useFunctionService(const std::string &functionName, std::function<void(F &function, const celix::Properties &props, const celix::IResourceBundle &owner)> use, const std::string &filter = "") const noexcept {
+ return registry().useFunctionService<F>(functionName, std::move(use), filter, bundle());
+ }
+
+ template<typename I>
+ int useServices(std::function<void(I &svc)> use, const std::string &filter = "") const noexcept {
+ return registry().useServices<I>(std::move(use), filter, bundle());
+ }
+
+ template<typename I>
+ int useServices(std::function<void(I &svc, const celix::Properties &props)> use, const std::string &filter = "") const noexcept {
+ return registry().useServices<I>(std::move(use), filter, bundle());
+ }
+
+ template<typename I>
+ int useServices(std::function<void(I &svc, const celix::Properties &props, const celix::IResourceBundle &owner)> use, const std::string &filter = "") const noexcept {
+ return registry().useServices<I>(std::move(use), filter, bundle());
+ }
+
+ template<typename F>
+ int useFunctionServices(const std::string &functionName, std::function<void(F &function)> use, const std::string &filter = "") const noexcept {
+ return registry().useFunctionServices<F>(functionName, std::move(use), filter, bundle());
+ }
+
+ template<typename F>
+ int useFunctionServices(const std::string &functionName, std::function<void(F &function, const celix::Properties &props)> use, const std::string &filter = "") const noexcept {
+ return registry().useFunctionServices<F>(functionName, std::move(use), filter, bundle());
+ }
+
+ template<typename F>
+ int useFunctionServices(const std::string &functionName, std::function<void(F &function, const celix::Properties &props, const celix::IResourceBundle &owner)> use, const std::string &filter = "") const noexcept {
+ return registry().useFunctionServices<F>(functionName, std::move(use), filter, bundle());
+ }
+
+ //TODO use C services
+
+ template<typename I>
+ celix::ServiceTracker trackServices(celix::ServiceTrackerOptions<I> options = {}) {
+ return registry().trackServices<I>(std::move(options), bundle());
+ }
+
+ template<typename F>
+ celix::ServiceTracker trackFunctionServices(std::string functionName, celix::ServiceTrackerOptions<F> options = {}) {
+ return registry().trackFunctionServices<F>(functionName, std::move(options), bundle());
+ }
+
+ //TODO track C Services
+
+ //TODO track trackers
+
+ //TODO track c trackers
+ private:
+ virtual celix::ServiceRegistry& registry() const noexcept = 0;
+ virtual celix::ServiceRegistry& cRegistry() const noexcept = 0;
+ };
+}
+
+#endif //CXX_CELIX_IBUNDLECONTEXT_H
diff --git a/bundles/config_admin/service/private/include/framework_patch.h b/libs/framework_cxx/include/celix/api.h
similarity index 63%
copy from bundles/config_admin/service/private/include/framework_patch.h
copy to libs/framework_cxx/include/celix/api.h
index 98194e1..63b19d4 100644
--- a/bundles/config_admin/service/private/include/framework_patch.h
+++ b/libs/framework_cxx/include/celix/api.h
@@ -16,24 +16,14 @@
*specific language governing permissions and limitations
*under the License.
*/
-/*
- * framework_patch.h
- *
- * \date Aug 12, 2013
- * \author <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
- * \copyright Apache License, Version 2.0
- */
-
-
-#ifndef BUNDLE_PATCH_H_
-#define BUNDLE_PATCH_H_
-
-/* celix.framework.public */
-#include "celix_errno.h"
-#include "bundle.h"
-#include "service_reference.h"
+#ifndef CXX_CELIX_API_H
+#define CXX_CELIX_API_H
-celix_status_t bundle_getBundleLocation(bundle_pt bundle, const char **location);
+#include "celix/Constants.h"
+#include "celix/Filter.h"
+#include "celix/ServiceRegistry.h"
+#include "celix/Framework.h"
+#include "celix/IBundleActivator.h"
-#endif /* BUNDLE_PATCH_H_ */
+#endif //CXX_CELIX_API_H
diff --git a/libs/framework_cxx/src/BundleImpl.h b/libs/framework_cxx/src/BundleImpl.h
new file mode 100644
index 0000000..ab49636
--- /dev/null
+++ b/libs/framework_cxx/src/BundleImpl.h
@@ -0,0 +1,178 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#ifndef CXX_CELIX_IMPL_BUNDLE_H
+#define CXX_CELIX_IMPL_BUNDLE_H
+
+#include <glog/logging.h>
+
+#include "celix/IBundle.h"
+#include "celix/IBundleContext.h"
+
+namespace celix {
+namespace impl {
+
+ class BundleContext : public celix::IBundleContext {
+ public:
+ BundleContext(std::shared_ptr<celix::IBundle> _bnd) : bnd{std::move(_bnd)},
+ reg{&bnd->framework().registry(celix::CXX_LANG)},
+ cReg(&bnd->framework().registry(celix::C_LANG)){}
+
+ virtual ~BundleContext() = default;
+
+ std::shared_ptr<celix::IBundle> bundle() const noexcept override {
+ return bnd;
+ }
+
+ bool useBundle(long bndId, std::function<void(const celix::IBundle &bnd)> use) const noexcept override {
+ return bnd->framework().useBundle(bndId, std::move(use));
+ }
+
+ int useBundles(std::function<void(const celix::IBundle &bnd)> use, bool includeFrameworkBundle = true) const noexcept override {
+ return bnd->framework().useBundles(std::move(use), includeFrameworkBundle);
+ }
+ private:
+ celix::ServiceRegistry& registry() const noexcept override { return *reg; }
+ celix::ServiceRegistry& cRegistry() const noexcept override { return *cReg; }
+
+ const std::shared_ptr<celix::IBundle> bnd;
+ const std::shared_ptr<celix::ServiceRegistry> reg;
+ const std::shared_ptr<celix::ServiceRegistry> cReg;
+ };
+
+
+ class Bundle : public celix::IBundle {
+ public:
+ Bundle(long _bndId, celix::Framework *_fw, celix::Properties _manifest, std::shared_ptr<celix::IBundleActivator> _activator) :
+ bndId{_bndId}, fw{_fw}, bndManifest{std::move(_manifest)}, activator{std::move(_activator)} {
+ bndState.store(BundleState::INSTALLED, std::memory_order_release);
+ }
+
+ //resource part
+ bool has(const std::string&) const noexcept override { return false; } //TODO
+ bool isDir(const std::string&) const noexcept override { return false; } //TODO
+ bool isFile(const std::string&) const noexcept override { return false; } //TODO
+ std::vector<std::string> readDir(const std::string&) const noexcept override { return std::vector<std::string>{};} //TODO
+ const std::string& root() const noexcept override { //TODO
+ static std::string empty{};
+ return empty;
+ }
+
+ //bundle part
+ bool isFrameworkBundle() const noexcept override { return false; }
+
+ void* handle() const noexcept override { return nullptr; } //TODO
+
+ long id() const noexcept override { return bndId; }
+ const std::string& name() const noexcept override { return bndManifest.at(celix::MANIFEST_BUNDLE_NAME); }
+ const std::string& symbolicName() const noexcept override { return bndManifest.at(celix::MANIFEST_BUNDLE_SYMBOLIC_NAME); }
+ const std::string& group() const noexcept override { return bndManifest.at(celix::MANIFEST_BUNDLE_SYMBOLIC_NAME); }
+ const std::string& version() const noexcept override { return bndManifest.at(celix::MANIFEST_BUNDLE_VERSION); }
+ const celix::Properties& manifest() const noexcept override { return bndManifest;}
+ bool isValid() const noexcept override { return bndId >= 0; }
+ celix::Framework& framework() const noexcept override { return *fw; }
+
+ celix::BundleState state() const noexcept override {
+ return bndState.load(std::memory_order_acquire);
+ }
+
+ void setState(celix::BundleState state) {
+ bndState.store(state, std::memory_order_release);
+ }
+
+ private:
+ const long bndId;
+ celix::Framework * const fw;
+ const celix::Properties bndManifest;
+ const std::shared_ptr<celix::IBundleActivator> activator;
+ std::weak_ptr<celix::IBundleContext> context;
+
+ std::atomic<BundleState> bndState;
+ };
+
+ class BundleController {
+ public:
+ BundleController(
+ std::shared_ptr<celix::IBundleActivator> _act,
+ std::shared_ptr<celix::impl::Bundle> _bnd,
+ std::shared_ptr<celix::impl::BundleContext> _ctx) :
+ act{std::move(_act)}, bnd{std::move(_bnd)}, ctx{std::move(_ctx)} {}
+
+ //specific part
+ bool transitionTo(BundleState desired) {
+ bool success = false;
+ std::lock_guard<std::mutex> lck{mutex};
+ const BundleState state = bnd->state();
+ if (state == desired) {
+ //nop
+ success = true;
+ } else if (state == BundleState::INSTALLED && desired == BundleState::RESOLVED) {
+ //TODO create cache dir for bundle
+ bool resolved = act->resolve(ctx);
+ if (resolved) {
+ bnd->setState(BundleState::RESOLVED);
+ success = true;
+ } else {
+ LOG(WARNING) << "Transition to resolved state for bundle " << bnd->symbolicName() << " (" << bnd->id() << ") failed." << std::endl;
+ }
+
+ } else if (state == BundleState::RESOLVED && desired == BundleState::ACTIVE) {
+ bool started = act->start(ctx);
+ if (started) {
+ bnd->setState(BundleState::ACTIVE);
+ success = true;
+ } else {
+ LOG(WARNING) << "Transition to active state for bundle " << bnd->symbolicName() << " (" << bnd->id() << ") failed." << std::endl;
+ }
+ } else if (state == BundleState::ACTIVE && desired == BundleState::RESOLVED ) {
+ bool stopped = act->stop(ctx);
+ if (stopped) {
+ bnd->setState(BundleState::RESOLVED);
+ success = true;
+
+ //TODO use custom deleter to check this (use_count call is a race condition)
+ bool unique = ctx.use_count() == 1;
+ if (!unique) {
+ LOG(WARNING) << "Bundle Context is not unique. ";
+ LOG(WARNING) << "Check if there are still some dangling references to the context of the stopped bundle." << std::endl;
+ }
+ } else {
+ LOG(WARNING) << "Transition to resolved state for bundle " << bnd->symbolicName() << " (" << bnd->id() << ") failed." << std::endl;
+ }
+ } else {
+ //LOG(ERROR) << "Unexpected desired state " << desired << " from state " << bndState << std::endl;
+ LOG(ERROR) << "Unexpected desired/form state combination " << std::endl;
+ }
+ return success;
+ }
+
+ std::shared_ptr<celix::IBundleActivator> activator() const { return act; }
+ std::shared_ptr<celix::impl::Bundle> bundle() const { return bnd; }
+ std::shared_ptr<celix::impl::BundleContext> context() const { return ctx; }
+ private:
+ const std::shared_ptr<celix::IBundleActivator> act;
+ const std::shared_ptr<celix::impl::Bundle> bnd;
+ const std::shared_ptr<celix::impl::BundleContext> ctx;
+
+ mutable std::mutex mutex{};
+ };
+}
+};
+
+#endif //CXX_CELIX_IMPL_BUNDLE_H
diff --git a/libs/framework_cxx/src/Framework.cc b/libs/framework_cxx/src/Framework.cc
new file mode 100644
index 0000000..a224720
--- /dev/null
+++ b/libs/framework_cxx/src/Framework.cc
@@ -0,0 +1,350 @@
+#include <utility>
+
+#include <utility>
+
+/**
+ *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 <unordered_map>
+#include <mutex>
+#include <iostream>
+#include <set>
+#include <vector>
+
+#include <glog/logging.h>
+
+#include "celix/Framework.h"
+
+#include "BundleImpl.h"
+
+struct StaticBundleEntry {
+ const std::string symbolicName;
+ const celix::Properties manifest;
+ const std::function<celix::IBundleActivator*()> activatorFactory;
+};
+
+static struct {
+ std::mutex mutex{};
+ std::vector<StaticBundleEntry> bundles{};
+ std::set<celix::Framework *> frameworks{};
+} staticRegistry{};
+
+
+static void registerFramework(celix::Framework *fw);
+static void unregisterFramework(celix::Framework *fw);
+
+class celix::Framework::Impl : public IBundle {
+public:
+ Impl(celix::Framework *_fw) : fw{_fw}, bndManifest{createManifest()}, cwd{createCwd()} {}
+
+ ~Impl() {
+ stopFramework();
+ }
+
+ std::vector<long> listBundles(bool includeFrameworkBundle) const {
+ std::vector<long> result{};
+ if (includeFrameworkBundle) {
+ result.push_back(0L); //framework bundle id
+ }
+ std::lock_guard<std::mutex> lock{bundles.mutex};
+ for (auto &entry : bundles.entries) {
+ result.push_back(entry.first);
+ }
+ std::sort(result.begin(), result.end());//ensure that the bundles are order by bndId -> i.e. time of install
+ return result;
+ }
+
+ long installBundle(std::string symbolicName, std::shared_ptr<celix::IBundleActivator> activator, celix::Properties manifest, bool autoStart) {
+ //TODO on separate thread ?? specific bundle resolve thread ??
+ long bndId = -1L;
+ std::shared_ptr<celix::impl::BundleController> bndController{nullptr};
+ {
+ manifest[celix::MANIFEST_BUNDLE_SYMBOLIC_NAME] = symbolicName;
+ if (manifest.find(celix::MANIFEST_BUNDLE_NAME) == manifest.end()) {
+ manifest[celix::MANIFEST_BUNDLE_NAME] = symbolicName;
+ }
+ if (manifest.find(celix::MANIFEST_BUNDLE_VERSION) == manifest.end()) {
+ manifest[celix::MANIFEST_BUNDLE_NAME] = "0.0.0";
+ }
+ if (manifest.find(celix::MANIFEST_BUNDLE_GROUP) == manifest.end()) {
+ manifest[celix::MANIFEST_BUNDLE_GROUP] = "";
+ }
+
+ std::lock_guard<std::mutex> lck{bundles.mutex};
+ bndId = bundles.nextBundleId++;
+ auto bnd = std::shared_ptr<celix::impl::Bundle>{new celix::impl::Bundle{bndId, this->fw, std::move(manifest), std::move(activator)}};
+ auto ctx = std::shared_ptr<celix::impl::BundleContext>{new celix::impl::BundleContext{bnd}};
+ bndController = std::shared_ptr<celix::impl::BundleController>{new celix::impl::BundleController{activator, bnd, ctx}};
+ bundles.entries.emplace(std::piecewise_construct,
+ std::forward_as_tuple(bndId),
+ std::forward_as_tuple(bndController));
+
+ //TODO increase bnd entry usage
+ }
+
+ if (bndController) {
+ bool successful = bndController->transitionTo(BundleState::RESOLVED);
+ if (successful && autoStart) {
+ successful = bndController->transitionTo(BundleState::ACTIVE);
+ if (!successful) {
+ LOG(WARNING) << "Cannot start bundle " << bndController->bundle()->symbolicName() << std::endl;
+ }
+ } else {
+ LOG(WARNING) << "Cannot resolve bundle " << bndController->bundle()->symbolicName() << std::endl;
+ }
+ //TODO decrease bnd entry usage
+ }
+ return bndId;
+ }
+
+ bool startBundle(long bndId) {
+ return transitionBundleTo(bndId, BundleState::ACTIVE);
+ }
+
+ bool stopBundle(long bndId) {
+ return transitionBundleTo(bndId, BundleState::RESOLVED);
+ }
+
+ bool uninstallBundle(long bndId) {
+ bool uninstalled = false;
+ std::shared_ptr<celix::impl::BundleController> removed{nullptr};
+ {
+ std::lock_guard<std::mutex> lck{bundles.mutex};
+ auto it = bundles.entries.find(bndId);
+ if (it != bundles.entries.end()) {
+ removed = std::move(it->second);
+ bundles.entries.erase(it);
+
+ }
+ }
+ if (removed) {
+ bool resolved = removed->transitionTo(BundleState::RESOLVED);
+ if (resolved) {
+ uninstalled = true;
+ bool unique = removed.unique();
+ assert(unique); //TODO cond / wait ?
+ } else {
+ //add bundle again -> not uninstalled
+ std::lock_guard<std::mutex> lck{bundles.mutex};
+ bundles.entries[bndId] = std::move(removed);
+ }
+ }
+ return uninstalled;
+ }
+
+ bool transitionBundleTo(long bndId, BundleState desired) {
+ bool successful = false;
+ std::shared_ptr<celix::impl::BundleController> match{nullptr};
+ {
+ std::lock_guard<std::mutex> lck{bundles.mutex};
+ auto it = bundles.entries.find(bndId);
+ if (it != bundles.entries.end()) {
+ match = it->second;
+ }
+ }
+ if (match) {
+ successful = match->transitionTo(desired);
+ }
+ return successful;
+ }
+
+ bool useBundle(long bndId, std::function<void(const celix::IBundle &bnd)> use) const {
+ bool called = false;
+ if (bndId == 0) {
+ //framework bundle
+ use(*this);
+ called = true;
+ } else {
+ std::shared_ptr<celix::impl::BundleController> match = nullptr;
+ {
+ std::lock_guard<std::mutex> lck{bundles.mutex};
+ auto it = bundles.entries.find(bndId);
+ if (it != bundles.entries.end()) {
+ match = it->second;
+ //TODO increase usage
+ }
+ }
+ if (match) {
+ use(*match->bundle());
+ called = true;
+ //TODO decrease usage -> use shared ptr instead
+ }
+ }
+ return called;
+ }
+
+ int useBundles(std::function<void(const celix::IBundle &bnd)> use, bool includeFramework) const {
+ std::map<long, std::shared_ptr<celix::impl::BundleController>> useBundles{};
+ {
+ std::lock_guard<std::mutex> lck{bundles.mutex};
+ for (const auto &it : bundles.entries) {
+ useBundles[it.first] = it.second;
+ }
+ }
+
+ if (includeFramework) {
+ use(*this);
+ }
+ for (const auto &cntr : useBundles) {
+ use(*cntr.second->bundle());
+ }
+ int count = (int)useBundles.size();
+ if (includeFramework) {
+ count += 1;
+ }
+ return count;
+ }
+
+ celix::ServiceRegistry& registry(const std::string &lang) {
+ std::lock_guard<std::mutex> lck{registries.mutex};
+ auto it = registries.entries.find(lang);
+ if (it == registries.entries.end()) {
+ registries.entries.emplace(std::string{lang}, celix::ServiceRegistry{std::string{lang}});
+ return registries.entries.at(lang);
+ } else {
+ return it->second;
+ }
+ }
+
+ //resource bundle part
+ long id() const noexcept override { return 1L /*note registry empty bundle is id 0, framework is id 1*/; }
+ bool has(const std::string&) const override { return false; }
+ bool isDir(const std::string&) const override { return false; }
+ bool isFile(const std::string&) const override { return false; }
+ std::vector<std::string> readDir(const std::string&) const override { return std::vector<std::string>{};}
+ const std::string& root() const noexcept override { //TODO
+ return cwd;
+ }
+
+ //bundle stuff
+ bool isFrameworkBundle() const noexcept override { return true; }
+ void* handle() const noexcept override { return nullptr; }
+ celix::BundleState state() const noexcept override { return BundleState::ACTIVE ; }
+ const std::string& name() const noexcept override { return bndManifest.at(celix::MANIFEST_BUNDLE_NAME); }
+ const std::string& symbolicName() const noexcept override { return bndManifest.at(celix::MANIFEST_BUNDLE_SYMBOLIC_NAME); }
+ const std::string& group() const noexcept override { return bndManifest.at(celix::MANIFEST_BUNDLE_GROUP); }
+ const std::string& version() const noexcept override { return bndManifest.at(celix::MANIFEST_BUNDLE_VERSION); }
+ const celix::Properties& manifest() const noexcept override { return bndManifest;}
+ bool isValid() const noexcept override { return true; }
+ celix::Framework& framework() const noexcept override { return *fw; }
+
+ bool stopFramework() {
+ std::vector<long> bundles = listBundles(false);
+ while (!bundles.empty()) {
+ for (auto it = bundles.rbegin(); it != bundles.rend(); ++it) {
+ stopBundle(*it);
+ uninstallBundle(*it);
+ }
+ bundles = listBundles(false);
+ }
+
+ return true;
+ }
+private:
+ celix::Properties createManifest() {
+ celix::Properties m{};
+ m[celix::MANIFEST_BUNDLE_SYMBOLIC_NAME] = "framework";
+ m[celix::MANIFEST_BUNDLE_NAME] = "Framework";
+ m[MANIFEST_BUNDLE_GROUP] = "Celix";
+ m[celix::MANIFEST_BUNDLE_VERSION] = "3.0.0";
+ return m;
+ }
+
+ std::string createCwd() {
+ char workdir[PATH_MAX];
+ if (getcwd(workdir, sizeof(workdir)) != NULL) {
+ return std::string{workdir};
+ } else {
+ return std::string{};
+ }
+ }
+
+ celix::Framework * const fw;
+ const celix::Properties bndManifest;
+ const std::string cwd;
+
+ struct {
+ std::unordered_map<long, std::shared_ptr<celix::impl::BundleController>> entries{};
+ long nextBundleId = 2;
+ mutable std::mutex mutex{};
+ } bundles{};
+
+ struct {
+ mutable std::mutex mutex{};
+ std::unordered_map<std::string, celix::ServiceRegistry> entries;
+ } registries;
+};
+
+/***********************************************************************************************************************
+ * Framework
+ **********************************************************************************************************************/
+
+celix::Framework::Framework() {
+ pimpl = std::unique_ptr<Impl>{new Impl{this}};
+ registerFramework(this); //TODO improve ugly.. maybe register impl.. but that is private -> so make register static member functions of impl...
+}
+celix::Framework::~Framework() {
+ unregisterFramework(this);
+}
+celix::Framework::Framework(Framework &&rhs) = default;
+celix::Framework& celix::Framework::operator=(Framework&& rhs) = default;
+
+long celix::Framework::installBundle(std::string name, std::shared_ptr<celix::IBundleActivator> activator, celix::Properties manifest, bool autoStart) {
+ return pimpl->installBundle(std::move(name), std::move(activator), std::move(manifest), autoStart);
+}
+
+std::vector<long> celix::Framework::listBundles(bool includeFrameworkBundle) const { return pimpl->listBundles(includeFrameworkBundle); }
+
+bool celix::Framework::useBundle(long bndId, std::function<void(const celix::IBundle &bnd)> use) const {
+ return pimpl->useBundle(bndId, use);
+}
+
+int celix::Framework::useBundles(std::function<void(const celix::IBundle &bnd)> use, bool includeFrameworkBundle) const {
+ return pimpl->useBundles(use, includeFrameworkBundle);
+}
+
+bool celix::Framework::startBundle(long bndId) { return pimpl->stopBundle(bndId); }
+bool celix::Framework::stopBundle(long bndId) { return pimpl->stopBundle(bndId); }
+bool celix::Framework::uninstallBundle(long bndId) { return pimpl->uninstallBundle(bndId); }
+celix::ServiceRegistry& celix::Framework::registry(const std::string &lang) { return pimpl->registry(lang); }
+
+/***********************************************************************************************************************
+ * Celix 'global' functions
+ **********************************************************************************************************************/
+
+void celix::registerStaticBundle(std::string symbolicName, const celix::StaticBundleOptions &opts) {
+ std::lock_guard<std::mutex> lck{staticRegistry.mutex};
+ staticRegistry.bundles.emplace_back(StaticBundleEntry{.symbolicName = std::move(symbolicName), .manifest = opts.manifest, .activatorFactory = opts.bundleActivatorFactory});
+ for (auto fw : staticRegistry.frameworks) {
+ fw->installBundle(symbolicName, std::shared_ptr<celix::IBundleActivator>{opts.bundleActivatorFactory()}, opts.manifest);
+ }
+}
+
+static void registerFramework(celix::Framework *fw) {
+ std::lock_guard<std::mutex> lck{staticRegistry.mutex};
+ staticRegistry.frameworks.insert(fw);
+ for (auto &entry : staticRegistry.bundles) {
+ fw->installBundle(entry.symbolicName, std::shared_ptr<celix::IBundleActivator>{entry.activatorFactory()}, entry.manifest);
+ }
+}
+
+static void unregisterFramework(celix::Framework *fw) {
+ std::lock_guard<std::mutex> lck{staticRegistry.mutex};
+ staticRegistry.frameworks.erase(fw);
+}
\ No newline at end of file
diff --git a/libs/CMakeLists.txt b/libs/registry/CMakeLists.txt
similarity index 64%
copy from libs/CMakeLists.txt
copy to libs/registry/CMakeLists.txt
index 5df35a2..ee537cc 100644
--- a/libs/CMakeLists.txt
+++ b/libs/registry/CMakeLists.txt
@@ -15,17 +15,18 @@
# specific language governing permissions and limitations
# under the License.
-#utils, dfi and etcdlib are standalone
-#(e.g. no dependency on celix framework
-add_subdirectory(utils)
-add_subdirectory(dfi)
-add_subdirectory(etcdlib)
+find_package(glog REQUIRED)
-add_subdirectory(framework)
+add_library(celix_registry STATIC
+ src/ServiceRegistry.cc
+ src/Filter.cc
+)
+target_include_directories(celix_registry PRIVATE src)
+target_include_directories(celix_registry PUBLIC include)
+target_link_libraries(celix_registry PRIVATE glog::glog) #TODO make glog static and private
-#launcher
-add_subdirectory(launcher)
+add_library(celix::registry ALIAS celix_registry)
-#add_subdirectory(event_admin)# event_admin is unstable
-add_subdirectory(dependency_manager)
-add_subdirectory(dependency_manager_cxx)
\ No newline at end of file
+if (ENABLE_TESTING)
+ add_subdirectory(gtest)
+endif ()
\ No newline at end of file
diff --git a/libs/CMakeLists.txt b/libs/registry/gtest/CMakeLists.txt
similarity index 62%
copy from libs/CMakeLists.txt
copy to libs/registry/gtest/CMakeLists.txt
index 5df35a2..80f0159 100644
--- a/libs/CMakeLists.txt
+++ b/libs/registry/gtest/CMakeLists.txt
@@ -5,9 +5,9 @@
# 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
@@ -15,17 +15,16 @@
# specific language governing permissions and limitations
# under the License.
-#utils, dfi and etcdlib are standalone
-#(e.g. no dependency on celix framework
-add_subdirectory(utils)
-add_subdirectory(dfi)
-add_subdirectory(etcdlib)
+find_package(glog REQUIRED)
-add_subdirectory(framework)
+set(SOURCES
+ src/main.cc
+ src/Registry_tests.cc
+ src/Filter_tests.cc
+ src/ServiceTracking_tests.cc
+)
+add_executable(celix_registry_tests ${SOURCES})
+target_link_libraries(celix_registry_tests PRIVATE gtest glog::glog celix::registry)
-#launcher
-add_subdirectory(launcher)
-
-#add_subdirectory(event_admin)# event_admin is unstable
-add_subdirectory(dependency_manager)
-add_subdirectory(dependency_manager_cxx)
\ No newline at end of file
+add_test(NAME celix_registry_tests COMMAND celix_registry_tests)
+SETUP_TARGET_FOR_COVERAGE(celix_registry_tests_cov celix_registry_tests ${CMAKE_BINARY_DIR}/coverage/registry)
\ No newline at end of file
diff --git a/libs/registry/gtest/src/Filter_tests.cc b/libs/registry/gtest/src/Filter_tests.cc
new file mode 100644
index 0000000..0e481db
--- /dev/null
+++ b/libs/registry/gtest/src/Filter_tests.cc
@@ -0,0 +1,207 @@
+/**
+ *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/Filter.h"
+
+class FilterTest : public ::testing::Test {
+public:
+ FilterTest() {}
+ ~FilterTest(){}
+};
+
+TEST_F(FilterTest, CreateFilterTest) {
+ const char *input1 = "(test_attr1=attr1)";
+ celix::Filter filter{input1};
+ EXPECT_TRUE(filter.valid());
+ EXPECT_EQ("test_attr1", filter.criteria->attribute);
+ EXPECT_EQ(celix::FilterOperator::EQUAL, filter.criteria->op);
+ EXPECT_EQ("attr1", filter.criteria->value);
+ EXPECT_EQ(0, filter.criteria->subcriteria.size());
+
+
+ const char *input2 = "(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=attr3)))";
+ celix::Filter filter2{input2};
+ EXPECT_TRUE(filter2.valid());
+ EXPECT_EQ(input2, filter2.filterStr);
+ EXPECT_EQ(celix::FilterOperator::AND, filter2.criteria->op);
+ EXPECT_EQ(2, filter2.criteria->subcriteria.size());
+ EXPECT_EQ(2, filter2.criteria->subcriteria[1]->subcriteria.size());
+ EXPECT_EQ("test_attr1", filter2.criteria->subcriteria[0]->attribute);
+ EXPECT_EQ(celix::FilterOperator::EQUAL, filter2.criteria->subcriteria[0]->op);
+ EXPECT_EQ("attr1", filter2.criteria->subcriteria[0]->value);
+
+ //test last criteria
+ EXPECT_EQ("test_attr3", filter2.criteria->subcriteria[1]->subcriteria[1]->attribute);
+ EXPECT_EQ(celix::FilterOperator::EQUAL, filter2.criteria->subcriteria[1]->subcriteria[1]->op);
+ EXPECT_EQ("attr3", filter2.criteria->subcriteria[1]->subcriteria[1]->value);
+ EXPECT_EQ(0, filter2.criteria->subcriteria[1]->subcriteria[1]->subcriteria.size());
+
+
+ //creation using a const char* literal and additional white spaces
+ celix::Filter filter3 = " ( & (test_attr1=attr1) ( | (test_attr2=attr2) (test_attr3=attr3 ) ) ) ";
+ filter3.valid();
+
+
+ //test PRESENT operator with trailing chars (should just register as substrings: "*" and "attr3")
+ celix::Filter f3 = "(test_attr3=*attr3)";
+ EXPECT_TRUE(f3.valid());
+ EXPECT_EQ(celix::FilterOperator::SUBSTRING, f3.criteria->op);
+ EXPECT_EQ("*attr3", f3.criteria->value);
+
+ //test parsing a value with a escaped closing bracket "\)"
+ celix::Filter f4 = "(test_attr3>=strWith\\)inIt)";
+ EXPECT_TRUE(f4.valid());
+ EXPECT_EQ(celix::FilterOperator::GREATER_EQUAL, f4.criteria->op);
+ EXPECT_EQ("strWith)inIt", f4.criteria->value);
+
+ //test parsing a equal with a escaped closing bracket "\)"
+ celix::Filter f5 = "(test_attr3=strWith\\)inIt)";
+ EXPECT_TRUE(f5.valid());
+ EXPECT_EQ(celix::FilterOperator::EQUAL, f5.criteria->op);
+ EXPECT_EQ("strWith)inIt", f5.criteria->value);
+
+ //test parsing a substring with a escaped closing bracket "\)"
+ celix::Filter f6 = "(test_attr3=*strWith\\)inIt)";
+ EXPECT_TRUE(f6.valid());
+ EXPECT_EQ(celix::FilterOperator::SUBSTRING, f6.criteria->op);
+ EXPECT_EQ("*strWith)inIt", f6.criteria->value);
+
+ celix::Filter f7 = ""; //empty is considered a valid filter
+ f7.valid();
+}
+
+TEST_F(FilterTest, CreateInvalidFilterTest) {
+
+ auto check = [](const char *filter) {
+ celix::Filter f = filter;
+ EXPECT_FALSE(f.valid());
+ };
+
+ check("no-parentheses");
+
+ //empty with whitespaces is also an invalid filter.
+ check(" ");
+
+ check("(attr=trailing_stuff) fdafa");
+
+ //test missing opening brackets in main filter
+ check("&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=attr3))");
+
+ //test missing opening brackets in AND comparator
+ check("(&test_attr1=attr1|(test_attr2=attr2)(test_attr3=attr3))");
+
+ //test missing opening brackets in AND comparator
+ check("(&(test_attr1=attr1)(|test_attr2=attr2(test_attr3=attr3))");
+
+ //test missing opening brackets in NOT comparator
+ check("(&(test_attr1=attr1)(!test_attr2=attr2)");
+
+ //test missing closing brackets in substring
+ check("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=attr3");
+
+ //test missing closing brackets in value
+ check("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3>=attr3");
+
+ //test missing closing brackets in substring
+ check("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=at(tr3)))");
+
+ //test missing closing brackets in value
+ check("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3>=att(r3)))");
+
+ //test trailing chars
+ check("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3=attr3))) oh no! trailing chars");
+
+ //test half APPROX operator (should be "~=", instead is "~")
+ check("(&(test_attr1=attr1)(|(test_attr2=attr2)(test_attr3~attr3)))");;
+
+ //test parsing a attribute of 0 length
+ check("(>=attr3)");
+
+ //test parsing a value of 0 length
+ check("(test_attr3>=)");
+}
+
+TEST_F(FilterTest, MatchComparators) {
+ celix::Properties props{};
+
+ props["test_attr1"] = "attr1";
+ props["test_attr2"] = "attr2";
+
+ //test present
+ {
+ celix::Filter f1 = "(test_attr1=*)"; //match true
+ celix::Filter f2 = "(test_attr3=*)"; //match false
+ EXPECT_TRUE(f1.valid());
+ EXPECT_TRUE(f2.valid());
+ EXPECT_TRUE(f1.match(props));
+ EXPECT_FALSE(f2.match(props));
+ }
+
+ //test equal
+ {
+ celix::Filter f1 = "(test_attr1=attr1)"; //match true
+ celix::Filter f2 = "(test_attr1=bla)"; //match false
+ EXPECT_TRUE(f1.valid());
+ EXPECT_TRUE(f2.valid());
+ EXPECT_TRUE(f1.match(props));
+ EXPECT_FALSE(f2.match(props));
+ }
+
+ //test not
+ {
+ celix::Filter f1 = "(!(test_attr1=bla))"; //match true
+ celix::Filter f2 = "(!(test_attr1=attr1))"; //match false
+ EXPECT_TRUE(f1.valid());
+ EXPECT_TRUE(f2.valid());
+ EXPECT_TRUE(f1.match(props));
+ EXPECT_FALSE(f2.match(props));
+ }
+
+ //test AND
+ {
+ celix::Filter f1 = "(&(test_attr1=attr1)(test_attr2=attr2))"; //match true
+ celix::Filter f2 = "(&(test_attr1=attr1)(test_attr3=attr3))"; //match false
+ EXPECT_TRUE(f1.valid());
+ EXPECT_TRUE(f2.valid());
+ EXPECT_TRUE(f1.match(props));
+ EXPECT_FALSE(f2.match(props));
+ }
+
+ //test OR
+ {
+ celix::Filter f1 = "(|(test_attr3=attr3)(test_attr2=attr2))"; //match true
+ celix::Filter f2 = "(|(test_attr4=attr4)(test_attr3=attr3))"; //match false
+ EXPECT_TRUE(f1.valid());
+ EXPECT_TRUE(f2.valid());
+ EXPECT_TRUE(f1.match(props));
+ EXPECT_FALSE(f2.match(props));
+ }
+
+ //test COMBINE
+ {
+ celix::Filter f1 = "(|(test_attr3=attr3)(&(!(test_attr4=*))(test_attr2=attr2)))"; //match true
+ celix::Filter f2 = "(|(test_attr3=attr3)(&(test_attr4=*)(test_attr2=attr2)))"; //match false
+ EXPECT_TRUE(f1.valid());
+ EXPECT_TRUE(f2.valid());
+ EXPECT_TRUE(f1.match(props));
+ EXPECT_FALSE(f2.match(props));
+ }
+}
diff --git a/libs/registry/gtest/src/RegistryConcurrency_tests.cc b/libs/registry/gtest/src/RegistryConcurrency_tests.cc
new file mode 100644
index 0000000..c77c198
--- /dev/null
+++ b/libs/registry/gtest/src/RegistryConcurrency_tests.cc
@@ -0,0 +1,75 @@
+/**
+ *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/ServiceRegistry.h"
+#include "celix/Constants.h"
+
+class RegistryConcurrentcyTest : public ::testing::Test {
+public:
+ RegistryConcurrentcyTest() {}
+ ~RegistryConcurrentcyTest(){}
+
+ celix::ServiceRegistry& registry() { return reg; }
+private:
+ celix::ServiceRegistry reg{"C/C++"};
+};
+
+class ICalc {
+public:
+ virtual ~ICalc() = default;
+ virtual double calc();
+};
+
+class NodeCalc : public ICalc {
+public:
+ virtual ~NodeCalc() = default;
+
+ double calc() override {
+ double val = 1.0;
+ std::lock_guard<std::mutex> lck{mutex};
+ for (auto *calc : childern) {
+ val *= calc->calc();
+ }
+ return val;
+ }
+private:
+ std::mutex mutex{}
+ std::vector<ICalc*> childern{};
+};
+
+class LeafCalc : public ICalc {
+public:
+ virtual ~LeafCalc() = default;
+
+ double calc() override {
+ return seed;
+ }
+private:
+ double const seed = std::rand() / 100.0;
+};
+
+
+TEST_F(RegistryConcurrentcyTest, ServiceRegistrationTest) {
+//TODO start many thread and cals using the tiers of other calcs eventually leading ot leafs and try to break the registry.
+//tier 1 : NodeCalc
+//tier 2 : NodeCalc
+//tier 3 : LeafCalc
+}
diff --git a/libs/registry/gtest/src/Registry_tests.cc b/libs/registry/gtest/src/Registry_tests.cc
new file mode 100644
index 0000000..8c00b11
--- /dev/null
+++ b/libs/registry/gtest/src/Registry_tests.cc
@@ -0,0 +1,269 @@
+/**
+ *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 <glog/logging.h>
+
+#include "celix/ServiceRegistry.h"
+
+class RegistryTest : public ::testing::Test {
+public:
+ RegistryTest() {}
+ ~RegistryTest(){}
+
+ celix::ServiceRegistry& registry() { return reg; }
+private:
+ celix::ServiceRegistry reg{"C/C++"};
+};
+
+class MarkerInterface1 {
+ //nop
+};
+class MarkerInterface2 {
+ //nop
+};
+class MarkerInterface3 {
+ //nop
+};
+
+
+TEST_F(RegistryTest, ServiceRegistrationTest) {
+ //empty registry
+ EXPECT_EQ(0, registry().nrOfRegisteredServices());
+
+ MarkerInterface1 intf1{};
+ {
+ celix::Properties props{};
+ props["TEST"] = "VAL";
+ celix::ServiceRegistration reg = registry().registerService(intf1, props);
+ //no copy possible celix::ServiceRegistration copy = reg;
+
+ long svcId = reg.serviceId();
+ EXPECT_GE(svcId, -1L);
+ EXPECT_TRUE(reg.valid());
+ EXPECT_EQ(reg.properties().at(celix::SERVICE_NAME), celix::serviceName<MarkerInterface1>());
+ EXPECT_EQ("VAL", celix::getProperty(reg.properties(), "TEST", ""));
+
+ EXPECT_EQ(1, registry().nrOfRegisteredServices());
+ EXPECT_EQ(1, registry().findServices<MarkerInterface1>().size());
+
+
+
+ celix::ServiceRegistration moved = std::move(reg); //moving is valid
+ EXPECT_EQ(svcId, moved.serviceId());
+ EXPECT_EQ(1, registry().nrOfRegisteredServices());
+
+ celix::ServiceRegistration moved2{std::move(moved)};
+ EXPECT_EQ(1, registry().nrOfRegisteredServices());
+
+ //out of scope -> deregister
+ }
+ EXPECT_EQ(0, registry().nrOfRegisteredServices());
+ EXPECT_EQ(0, registry().findServices<MarkerInterface1>().size());
+
+ MarkerInterface1 intf2{};
+ MarkerInterface2 intf3{};
+ {
+ celix::ServiceRegistration reg1 = registry().registerService(intf1);
+ EXPECT_EQ(1, registry().nrOfRegisteredServices());
+ celix::ServiceRegistration reg2 = registry().registerService(intf2);
+ EXPECT_EQ(2, registry().nrOfRegisteredServices());
+ celix::ServiceRegistration reg3 = registry().registerService(intf3);
+ EXPECT_EQ(3, registry().nrOfRegisteredServices());
+
+ EXPECT_GT(reg1.serviceId(), 0);
+ EXPECT_GT(reg2.serviceId(), 0);
+ EXPECT_GT(reg3.serviceId(), 0);
+ EXPECT_TRUE(reg1.valid());
+ EXPECT_TRUE(reg2.valid());
+ EXPECT_TRUE(reg3.valid());
+
+ EXPECT_EQ(3, registry().nrOfRegisteredServices());
+ EXPECT_EQ(2, registry().findServices<MarkerInterface1>().size());
+ EXPECT_EQ(1, registry().findServices<MarkerInterface2>().size());
+ EXPECT_EQ(0, registry().findServices<MarkerInterface3>().size());
+
+ //out of scope -> deregister
+ }
+ EXPECT_EQ(0, registry().nrOfRegisteredServices());
+ EXPECT_EQ(0, registry().findServices<MarkerInterface1>().size());
+ EXPECT_EQ(0, registry().findServices<MarkerInterface2>().size());
+ EXPECT_EQ(0, registry().findServices<MarkerInterface3>().size());
+}
+
+TEST_F(RegistryTest, SimpleFindServicesTest) {
+ MarkerInterface1 intf1{};
+ MarkerInterface2 intf2{};
+
+ auto reg1 = registry().registerService(intf1);
+ auto reg2 = registry().registerService(intf2);
+
+ long firstSvc = registry().findService<MarkerInterface1>();
+ EXPECT_GT(firstSvc, 0);
+ std::vector<long> services = registry().findServices<MarkerInterface1>();
+ EXPECT_EQ(1, services.size());
+
+ auto reg3 = registry().registerService(intf1);
+ auto reg4 = registry().registerService(intf2);
+
+ long foundSvc = registry().findService<MarkerInterface1>(); //should find the first services
+ EXPECT_GT(foundSvc, 0);
+ EXPECT_EQ(firstSvc, foundSvc);
+ services = registry().findServices<MarkerInterface1>();
+ EXPECT_EQ(2, services.size());
+}
+
+TEST_F(RegistryTest, FindServicesTest) {
+ MarkerInterface1 intf1{};
+
+ celix::Properties props1{};
+ props1["loc"] = "front";
+ props1["answer"] = "42";
+
+ celix::Properties props2{};
+ props2["loc"] = "back";
+
+ auto reg1 = registry().registerService(intf1, props1);
+ auto reg2 = registry().registerService(intf1, std::move(props2));
+ auto reg3 = registry().registerService(intf1);
+
+ auto find1 = registry().findServices<MarkerInterface1>("(loc=*)"); // expecting 2
+ auto find2 = registry().findServices<MarkerInterface1>("(answer=42)"); // expecting 1
+ auto find3 = registry().findServices<MarkerInterface1>("(!(loc=*))"); // expecting 1
+
+ EXPECT_EQ(2, find1.size());
+ EXPECT_EQ(1, find2.size());
+ EXPECT_EQ(1, find3.size());
+}
+
+TEST_F(RegistryTest, UseServices) {
+ MarkerInterface1 intf1{};
+ auto reg1 = registry().registerService(intf1);
+ auto reg2 = registry().registerService(intf1);
+ auto reg3 = registry().registerService(intf1);
+
+ long svcId1 = reg1.serviceId();
+
+ bool called = registry().useService<MarkerInterface1>([&](MarkerInterface1& svc) -> void {
+ EXPECT_EQ(&svc, &intf1);
+ });
+ EXPECT_TRUE(called);
+
+ called = registry().useService<MarkerInterface1>([&](MarkerInterface1& svc, const celix::Properties &props) -> void {
+ EXPECT_EQ(&svc, &intf1);
+ long id = celix::getProperty(props, celix::SERVICE_ID, 0);
+ EXPECT_EQ(svcId1, id);
+ });
+ EXPECT_TRUE(called);
+
+ called = registry().useService<MarkerInterface1>([&](MarkerInterface1& svc, const celix::Properties &props, const celix::IResourceBundle &bnd) -> void {
+ EXPECT_EQ(&svc, &intf1);
+ long id = celix::getProperty(props, celix::SERVICE_ID, 0);
+ EXPECT_EQ(svcId1, id);
+ EXPECT_EQ(0, bnd.id()); //not nullptr -> use empty bundle (bndId 0)
+ });
+ EXPECT_TRUE(called);
+}
+
+TEST_F(RegistryTest, RankingTest) {
+ MarkerInterface1 intf1{};
+ auto reg1 = registry().registerService(intf1); //no props -> ranking 0
+ auto reg2 = registry().registerService(intf1); //no props -> ranking 0
+
+ {
+ auto reg3 = registry().registerService(intf1); //no props -> ranking 0
+ auto find = registry().findServices<MarkerInterface1>(); // expecting 3
+ EXPECT_EQ(3, find.size());
+ EXPECT_EQ(reg1.serviceId(), find[0]); //first registered service should be found first
+ EXPECT_EQ(reg2.serviceId(), find[1]); //first registered service should be found first
+ EXPECT_EQ(reg3.serviceId(), find[2]); //first registered service should be found first
+
+ //note reg 3 out of scope
+ }
+
+ celix::Properties props{};
+ props[celix::SERVICE_RANKING] = "100";
+ auto reg4 = registry().registerService(intf1, props); // ranking 100 -> before services with ranking 0
+
+ {
+ auto reg5 = registry().registerService(intf1, props); // ranking 100 -> before services with ranking 0
+ auto find = registry().findServices<MarkerInterface1>();
+ EXPECT_EQ(4, find.size());
+ EXPECT_EQ(reg4.serviceId(), find[0]); //ranking 100, oldest (i.e. highest ranking with lowest svcId)
+ EXPECT_EQ(reg5.serviceId(), find[1]); //ranking 100, newest
+ EXPECT_EQ(reg1.serviceId(), find[2]); //ranking 0, oldest
+ EXPECT_EQ(reg2.serviceId(), find[3]); //ranking 0, newest
+
+ //note reg 5 out of scope
+ }
+
+ props[celix::SERVICE_RANKING] = "-100";
+ auto reg6 = registry().registerService(intf1, props); // ranking -100 -> after the rest
+ auto reg7 = registry().registerService(intf1, props); // ranking -100 -> after the rest
+ props[celix::SERVICE_RANKING] = "110";
+ auto reg8 = registry().registerService(intf1, props); // ranking 110 -> before the rest
+ props[celix::SERVICE_RANKING] = "80";
+ auto reg9 = registry().registerService(intf1, props); // ranking 80 -> between ranking 0 adn 100
+ {
+ auto find = registry().findServices<MarkerInterface1>();
+ EXPECT_EQ(7, find.size());
+ EXPECT_EQ(reg8.serviceId(), find[0]); //ranking 110
+ EXPECT_EQ(reg4.serviceId(), find[1]); //ranking 100
+ EXPECT_EQ(reg9.serviceId(), find[2]); //ranking 80
+ EXPECT_EQ(reg1.serviceId(), find[3]); //ranking 0, oldest
+ EXPECT_EQ(reg2.serviceId(), find[4]); //ranking 0, newest
+ EXPECT_EQ(reg6.serviceId(), find[5]); //ranking -100, oldest
+ EXPECT_EQ(reg7.serviceId(), find[6]); //ranking -100, newest
+ }
+}
+
+TEST_F(RegistryTest, StdFunctionTest) {
+ int count = 0;
+ std::function<void()> func1 = [&count]{
+ count++;
+ };
+
+ auto reg1 = registry().registerFunctionService("count", func1);
+ LOG(INFO) << reg1.serviceName() << std::endl;
+ EXPECT_TRUE(reg1.valid());
+ EXPECT_EQ(1, registry().nrOfRegisteredServices());
+
+ std::function<int(long,double,const std::string&)> funcWithReturnAndArgs = [](long a, double b, const std::string &ref) -> int {
+ return (int)(a/b) + (int)ref.size();
+ };
+ auto reg2 = registry().registerFunctionService("yet another function", std::move(funcWithReturnAndArgs));
+ EXPECT_TRUE(reg2.valid());
+ EXPECT_EQ(2, registry().nrOfRegisteredServices());
+ LOG(INFO) << reg2.serviceName() << std::endl;
+
+ std::function<void(std::function<void()>&)> use = [](std::function<void()> &count) {
+ count();
+ };
+ EXPECT_EQ(0, count);
+ registry().useFunctionService("count", use);
+ EXPECT_EQ(1, count);
+ registry().useFunctionService<std::function<void()>>("count", [](std::function<void()> &count) {
+ count();
+ });
+}
+
+//TODO function use with props and bnd
+//TODO use with filter
+//TODO use with sync test (see BundleContext tests)
\ No newline at end of file
diff --git a/libs/registry/gtest/src/ServiceTracking_tests.cc b/libs/registry/gtest/src/ServiceTracking_tests.cc
new file mode 100644
index 0000000..7a88f1b
--- /dev/null
+++ b/libs/registry/gtest/src/ServiceTracking_tests.cc
@@ -0,0 +1,275 @@
+/**
+ *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 "gtest/gtest.h"
+
+#include "celix/ServiceRegistry.h"
+#include "celix/Constants.h"
+
+class ServiceTrackingTest : public ::testing::Test {
+public:
+ celix::ServiceRegistry& registry() { return reg; }
+private:
+ celix::ServiceRegistry reg{"C/C++"};
+};
+
+class MarkerInterface1 {
+ //nop
+};
+class MarkerInterface2 {
+ //nop
+};
+class MarkerInterface3 {
+ //nop
+};
+
+
+TEST_F(ServiceTrackingTest, CreateTrackersTest) {
+ EXPECT_EQ(0, registry().nrOfServiceTrackers());
+ {
+ celix::ServiceTracker trk1 = registry().trackServices<MarkerInterface1>();
+ EXPECT_EQ(1, registry().nrOfServiceTrackers());
+
+ celix::ServiceTracker moved = std::move(trk1);
+ EXPECT_EQ(1, registry().nrOfServiceTrackers());
+
+ celix::ServiceTracker moved2 = celix::ServiceTracker{std::move(moved)};
+ EXPECT_EQ(1, registry().nrOfServiceTrackers());
+
+ auto trk2 = registry().trackServices<MarkerInterface1>();
+ auto trk3 = registry().trackServices<MarkerInterface2>();
+ EXPECT_EQ(3, registry().nrOfServiceTrackers());
+ }
+ EXPECT_EQ(0, registry().nrOfServiceTrackers());
+}
+
+TEST_F(ServiceTrackingTest, ServicesCountTrackersTest) {
+ MarkerInterface1 intf1;
+ MarkerInterface2 intf2;
+ MarkerInterface3 intf3;
+
+ auto trk1 = registry().trackServices<MarkerInterface1>();
+ ASSERT_EQ(0, trk1.trackCount());
+
+ auto reg1 = registry().registerService(intf1);
+ auto reg2 = registry().registerService(intf2);
+
+ auto trk2 = registry().trackServices<MarkerInterface2>();
+ ASSERT_EQ(1, trk1.trackCount());
+ ASSERT_EQ(1, trk2.trackCount());
+
+ {
+
+ auto reg3 = registry().registerService(intf1);
+ ASSERT_EQ(2, trk1.trackCount());
+ ASSERT_EQ(1, trk2.trackCount());
+
+ {
+ auto reg4 = registry().registerService(intf1);
+ auto reg5 = registry().registerService(intf2);
+ auto reg6 = registry().registerService(intf3);
+
+ ASSERT_EQ(3, trk1.trackCount());
+ ASSERT_EQ(2, trk2.trackCount());
+
+ //out of scope -> services are deregistered
+ }
+
+ ASSERT_EQ(2, trk1.trackCount());
+ ASSERT_EQ(1, trk2.trackCount());
+ //out of scope -> services are deregistered
+ }
+
+ ASSERT_EQ(1, trk1.trackCount());
+ ASSERT_EQ(1, trk2.trackCount());
+}
+
+TEST_F(ServiceTrackingTest, SetServiceTest) {
+ MarkerInterface1 intf1;
+ MarkerInterface2 intf2;
+ MarkerInterface3 intf3;
+
+ MarkerInterface1 *ptrToSvc = nullptr;
+ //const celix::Properties *ptrToProps = nullptr;
+ //const celix::IBundle *ptrToBnd = nullptr;
+
+ celix::ServiceTrackerOptions<MarkerInterface1> opts{};
+ opts.set = [&ptrToSvc](MarkerInterface1* svc) {
+ ptrToSvc = svc;
+ };
+
+ auto reg1 = registry().registerService(intf1);
+ auto reg2 = registry().registerService(intf2);
+ auto reg3 = registry().registerService(intf3);
+
+ auto trk1 = registry().trackServices(opts);
+ EXPECT_EQ(&intf1, ptrToSvc); //should be intf1
+
+ reg1.unregister();
+ EXPECT_EQ(nullptr, ptrToSvc); //unregistered -> nullptr
+
+
+ reg1 = registry().registerService(intf1);
+ EXPECT_EQ(&intf1, ptrToSvc); //set to intf1 again
+
+ MarkerInterface1 intf4;
+ auto reg4 = registry().registerService(intf4);
+ EXPECT_EQ(&intf1, ptrToSvc); //no change -> intf1 is older
+
+ reg1.unregister();
+ EXPECT_EQ(&intf4, ptrToSvc); //intf1 is gone, intf4 should be set
+
+ trk1.stop();
+ EXPECT_EQ(nullptr, ptrToSvc); //stop tracking nullptr svc is set
+
+ {
+ auto trk2 = registry().trackServices(opts);
+ EXPECT_EQ(&intf4, ptrToSvc); //intf1 is gone, intf4 should be set
+ //out of scope -> tracker stopped
+ }
+ EXPECT_EQ(nullptr, ptrToSvc); //stop tracking nullptr svc is set
+}
+
+TEST_F(ServiceTrackingTest, SetServiceWithPropsAndOwnderTest) {
+ /*
+ MarkerInterface1 intf1;
+ MarkerInterface2 intf2;
+ MarkerInterface3 intf3;
+
+ MarkerInterface1 *ptrToSvc = nullptr;
+ const celix::Properties *ptrToProps = nullptr;
+ const celix::IBundle *ptrToBnd = nullptr;
+ */
+ //TODO
+}
+
+TEST_F(ServiceTrackingTest, AddRemoveTest) {
+ MarkerInterface1 intf1;
+ MarkerInterface2 intf2;
+ MarkerInterface3 intf3;
+
+ std::vector<MarkerInterface1*> services{};
+
+ celix::ServiceTrackerOptions<MarkerInterface1> opts{};
+ opts.add = [&services](MarkerInterface1* svc) {
+ services.push_back(svc);
+ };
+ opts.remove = [&services](MarkerInterface1* svc) {
+ services.erase(std::remove(services.begin(), services.end(), svc), services.end());
+ };
+
+ auto reg1 = registry().registerService(intf1);
+ auto reg2 = registry().registerService(intf2);
+ auto reg3 = registry().registerService(intf3);
+
+ auto trk1 = registry().trackServices(opts);
+ ASSERT_EQ(1, services.size());
+ EXPECT_EQ(&intf1, services[0]); //should be intf1
+
+ reg1.unregister();
+ EXPECT_EQ(0, services.size());
+
+ reg1 = registry().registerService(intf1);
+ ASSERT_EQ(1, services.size());
+ EXPECT_EQ(&intf1, services[0]); //should be intf1 again
+
+ MarkerInterface1 intf4{};
+ auto reg4 = registry().registerService(intf4);
+ ASSERT_EQ(2, services.size());
+ EXPECT_EQ(&intf1, services[0]);
+ EXPECT_EQ(&intf4, services[1]);
+
+ reg1.unregister();
+ ASSERT_EQ(1, services.size());
+ EXPECT_EQ(&intf4, services[0]); //intf1 gone -> index 0: intf4
+
+ trk1.stop();
+ EXPECT_EQ(0, services.size());
+
+ {
+ auto trk2 = registry().trackServices(opts);
+ ASSERT_EQ(1, services.size());
+ EXPECT_EQ(&intf4, services[0]);
+ //out of scope -> tracker stopped
+ }
+ EXPECT_EQ(0, services.size()); //stop tracking -> services removed
+}
+
+TEST_F(ServiceTrackingTest, AddRemoveServicesWithPropsAndOwnderTest) {
+ //TODO
+}
+
+TEST_F(ServiceTrackingTest, UpdateTest) {
+ MarkerInterface1 intf1;
+ MarkerInterface2 intf2;
+ MarkerInterface3 intf3;
+
+ std::vector<MarkerInterface1*> services{};
+
+ celix::ServiceTrackerOptions<MarkerInterface1> opts{};
+ opts.update = [&services](std::vector<MarkerInterface1*> rankedServices) {
+ services = rankedServices;
+ };
+
+ auto reg1 = registry().registerService(intf1);
+ auto reg2 = registry().registerService(intf2);
+ auto reg3 = registry().registerService(intf3);
+
+ auto trk1 = registry().trackServices(opts);
+ EXPECT_EQ(1, trk1.trackCount());
+ ASSERT_EQ(1, services.size());
+ EXPECT_EQ(&intf1, services[0]); //should be intf1
+
+ reg1.unregister();
+ EXPECT_EQ(0, services.size());
+
+ reg1 = registry().registerService(intf1);
+ ASSERT_EQ(1, services.size());
+ EXPECT_EQ(&intf1, services[0]); //should be intf1 again
+
+ MarkerInterface1 intf4{};
+ celix::Properties props{std::make_pair(celix::SERVICE_RANKING, "100")};
+ auto reg4 = registry().registerService(intf4, std::move(props));
+ ASSERT_EQ(2, services.size());
+ EXPECT_EQ(&intf4, services[0]); //note 4 higher ranking
+ EXPECT_EQ(&intf1, services[1]);
+
+ reg1.unregister();
+ ASSERT_EQ(1, services.size());
+ EXPECT_EQ(&intf4, services[0]); //intf1 gone -> index 0: intf4
+
+ trk1.stop();
+ EXPECT_EQ(0, services.size());
+
+ {
+ auto trk2 = registry().trackServices(opts);
+ ASSERT_EQ(1, services.size());
+ EXPECT_EQ(&intf4, services[0]);
+ //out of scope -> tracker stopped
+ }
+ EXPECT_EQ(0, services.size()); //stop tracking -> services removed
+}
+
+TEST_F(ServiceTrackingTest, UpdateTestWithPropsAndOwner) {
+ //TODO
+}
+
+//TODO function service tracking tests
diff --git a/bundles/config_admin/service/private/include/framework_patch.h b/libs/registry/gtest/src/main.cc
similarity index 63%
copy from bundles/config_admin/service/private/include/framework_patch.h
copy to libs/registry/gtest/src/main.cc
index 98194e1..a76daa7 100644
--- a/bundles/config_admin/service/private/include/framework_patch.h
+++ b/libs/registry/gtest/src/main.cc
@@ -16,24 +16,18 @@
*specific language governing permissions and limitations
*under the License.
*/
-/*
- * framework_patch.h
- *
- * \date Aug 12, 2013
- * \author <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
- * \copyright Apache License, Version 2.0
- */
-
-#ifndef BUNDLE_PATCH_H_
-#define BUNDLE_PATCH_H_
+#include <gtest/gtest.h>
+#include <glog/logging.h>
+int main(int argc, char **argv) {
+ google::InitGoogleLogging(argv[0]);
+ google::LogToStderr();
-/* celix.framework.public */
-#include "celix_errno.h"
-#include "bundle.h"
-#include "service_reference.h"
+ ::testing::InitGoogleTest(&argc, argv);
+ int rc = RUN_ALL_TESTS();
-celix_status_t bundle_getBundleLocation(bundle_pt bundle, const char **location);
+ google::ShutdownGoogleLogging();
-#endif /* BUNDLE_PATCH_H_ */
+ return rc;
+}
\ No newline at end of file
diff --git a/libs/registry/include/celix/Constants.h b/libs/registry/include/celix/Constants.h
new file mode 100644
index 0000000..f1fcad3
--- /dev/null
+++ b/libs/registry/include/celix/Constants.h
@@ -0,0 +1,43 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+
+#ifndef CXX_CELIX_CONSTANTS_H
+#define CXX_CELIX_CONSTANTS_H
+
+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 FRAMEWORK_UUID = "framework.uuid";
+
+ static constexpr const char *const CXX_LANG = "C++";
+ static constexpr const char *const C_LANG = "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";
+ static constexpr const char *const MANIFEST_BUNDLE_GROUP = "Bundle-Group";
+}
+
+#endif //CXX_CELIX_CONSTANTS_H
diff --git a/libs/registry/include/celix/Filter.h b/libs/registry/include/celix/Filter.h
new file mode 100644
index 0000000..db851ba
--- /dev/null
+++ b/libs/registry/include/celix/Filter.h
@@ -0,0 +1,73 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#ifndef CXX_CELIX_FILTER_H
+#define CXX_CELIX_FILTER_H
+
+#include <string>
+#include <vector>
+
+#include "celix/Properties.h"
+
+namespace celix {
+
+ enum class FilterOperator {
+ EQUAL,
+ APPROX,
+ GREATER,
+ GREATER_EQUAL,
+ LESS,
+ LESS_EQUAL,
+ PRESENT,
+ SUBSTRING,
+ AND,
+ OR,
+ NOT
+ };
+
+ struct FilterCriteria; //forward declr
+ struct FilterCriteria {
+ const std::string attribute;
+ const celix::FilterOperator op;
+ const std::string value;
+ const std::vector<std::shared_ptr<FilterCriteria>> subcriteria;
+ };
+
+ class Filter {
+ public:
+ Filter(const char* f) : filterStr{f}, criteria{parseFilter()} {}
+ Filter(std::string f) : filterStr{std::move(f)}, criteria{parseFilter()} {}
+ Filter(const Filter &rhs) = default;
+ Filter(Filter &&rhs) = default;
+
+ Filter& operator=(const Filter& rhs) = default;
+ Filter& operator=(Filter&& rhs) = default;
+
+ bool empty() const { return filterStr.empty(); }
+ bool valid() const { return filterStr.empty() || criteria != nullptr; }
+ bool match(const celix::Properties &props) const;
+
+ const std::string filterStr;
+ const std::shared_ptr<FilterCriteria> criteria;
+ private:
+ FilterCriteria* parseFilter();
+ };
+}
+
+#endif //CXX_CELIX_FILTER_H
diff --git a/libs/registry/include/celix/IResourceBundle.h b/libs/registry/include/celix/IResourceBundle.h
new file mode 100644
index 0000000..6352736
--- /dev/null
+++ b/libs/registry/include/celix/IResourceBundle.h
@@ -0,0 +1,57 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#ifndef CXX_CELIX_IRESOURCEBUNDLE_H
+#define CXX_CELIX_IRESOURCEBUNDLE_H
+
+namespace celix {
+
+ class IResourceBundle {
+ public:
+ virtual ~IResourceBundle() = default;
+
+ virtual long id() const noexcept = 0;
+
+ virtual const std::string& root() const noexcept = 0;
+
+ virtual bool has(const std::string &path) const noexcept = 0;
+ virtual bool isDir(const std::string &path) const noexcept = 0;
+ virtual bool isFile(const std::string &path) const noexcept = 0;
+
+ //virtual bool storeResource(const std::string &path, std::ostream content) noexcept = 0;
+ //virtual std::istream open(const std::string &path) const noexcept = 0;
+ //virtual std::fstream open(const std::string &path) noexcept = 0;
+ virtual std::vector<std::string> readDir(const std::string &path) const noexcept = 0;
+ };
+
+
+ /*
+ * void read_directory(const std::string& name, stringvec& v)
+{
+ DIR* dirp = opendir(name.c_str());
+ struct dirent * dp;
+ while ((dp = readdir(dirp)) != NULL) {
+ v.push_back(dp->d_name);
+ }
+ closedir(dirp);
+}
+ */
+}
+
+#endif //CXX_CELIX_IRESOURCEBUNDLE_H
diff --git a/bundles/config_admin/service/private/include/framework_patch.h b/libs/registry/include/celix/IServiceFactory.h
similarity index 57%
copy from bundles/config_admin/service/private/include/framework_patch.h
copy to libs/registry/include/celix/IServiceFactory.h
index 98194e1..af27312 100644
--- a/bundles/config_admin/service/private/include/framework_patch.h
+++ b/libs/registry/include/celix/IServiceFactory.h
@@ -16,24 +16,26 @@
*specific language governing permissions and limitations
*under the License.
*/
-/*
- * framework_patch.h
- *
- * \date Aug 12, 2013
- * \author <a href="mailto:dev@celix.apache.org">Apache Celix Project Team</a>
- * \copyright Apache License, Version 2.0
- */
+#ifndef CXX_CELIX_ISERVICEFACTORY_H
+#define CXX_CELIX_ISERVICEFACTORY_H
+
+#include "celix/Properties.h"
+#include "celix/IResourceBundle.h"
+
+namespace celix {
-#ifndef BUNDLE_PATCH_H_
-#define BUNDLE_PATCH_H_
+ template<typename I>
+ class IServiceFactory {
+ public:
+ using type = I;
+ virtual ~IServiceFactory() = default;
-/* celix.framework.public */
-#include "celix_errno.h"
-#include "bundle.h"
-#include "service_reference.h"
+ virtual I* getService(const celix::IResourceBundle &requestingBundle, const celix::Properties &properties) noexcept = 0;
+ virtual void ungetService(const celix::IResourceBundle &requestingBundle, const celix::Properties &properties) noexcept = 0;
+ };
-celix_status_t bundle_getBundleLocation(bundle_pt bundle, const char **location);
+}
-#endif /* BUNDLE_PATCH_H_ */
+#endif //CXX_CELIX_ISERVICEFACTORY_H
diff --git a/libs/registry/include/celix/Properties.h b/libs/registry/include/celix/Properties.h
new file mode 100644
index 0000000..65fd4ef
--- /dev/null
+++ b/libs/registry/include/celix/Properties.h
@@ -0,0 +1,60 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#ifndef CXX_CELIX_PROPERTIES_H
+#define CXX_CELIX_PROPERTIES_H
+
+#include <string>
+#include <map>
+
+namespace celix {
+
+ using Properties = std::map<std::string, std::string>;
+
+ inline const std::string& getProperty(const Properties& props, const std::string &key, const std::string &defaultValue) noexcept {
+ auto it = props.find(key);
+ if (it != props.end()) {
+ return props.at(key);
+ } else {
+ return defaultValue;
+ }
+ }
+
+ inline int getProperty(const Properties& props, const std::string &key, int defaultValue) noexcept {
+ std::string val = getProperty(props, key, std::to_string(defaultValue));
+ return std::stoi(val, nullptr, 10);
+ }
+
+ inline unsigned int getProperty(const Properties& props, const std::string &key, unsigned int defaultValue) noexcept {
+ std::string val = getProperty(props, key, std::to_string(defaultValue));
+ return static_cast<unsigned int>(std::stoul(val, nullptr, 10)); //NOTE no std::stou ??
+ }
+
+ inline long getProperty(const Properties& props, const std::string &key, long defaultValue) noexcept {
+ std::string val = getProperty(props, key, std::to_string(defaultValue));
+ return std::stol(val, nullptr, 10);
+ }
+
+ inline unsigned int getProperty(const Properties& props, const std::string &key, unsigned long defaultValue) noexcept {
+ std::string val = getProperty(props, key, std::to_string(defaultValue));
+ return std::stoul(val, nullptr, 10);
+ }
+}
+
+#endif //CXX_CELIX_PROPERTIES_H
diff --git a/libs/registry/include/celix/ServiceRegistry.h b/libs/registry/include/celix/ServiceRegistry.h
new file mode 100644
index 0000000..7bbe16c
--- /dev/null
+++ b/libs/registry/include/celix/ServiceRegistry.h
@@ -0,0 +1,509 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#ifndef CXX_CELIX_SERVICEREGISTRY_H
+#define CXX_CELIX_SERVICEREGISTRY_H
+
+#include <utility>
+#include <vector>
+
+#include "celix/Constants.h"
+#include "celix/Properties.h"
+#include "celix/Utils.h"
+#include "celix/IResourceBundle.h"
+#include "celix/IServiceFactory.h"
+
+namespace celix {
+
+ //forward declaration
+ class ServiceRegistry;
+
+ // RAII service registration: out of scope -> deregister service
+ // NOTE access not thread safe -> TODO make thread save?
+ class ServiceRegistration {
+ public:
+ ServiceRegistration();
+ ServiceRegistration(ServiceRegistration &&rhs) noexcept;
+ ServiceRegistration(const ServiceRegistration &rhs) = delete;
+ ~ServiceRegistration();
+ ServiceRegistration& operator=(ServiceRegistration &&rhs) noexcept;
+ ServiceRegistration& operator=(const ServiceRegistration &rhs) = delete;
+
+ long serviceId() const;
+ bool valid() const;
+ const celix::Properties& properties() const;
+ const std::string& serviceName() const;
+ bool factory() const;
+ bool registered() const;
+
+ void unregister();
+ private:
+ class Impl; //opaque impl class
+ std::unique_ptr<celix::ServiceRegistration::Impl> pimpl;
+ explicit ServiceRegistration(celix::ServiceRegistration::Impl *impl);
+ friend ServiceRegistry;
+ };
+
+ template<typename I>
+ struct ServiceTrackerOptions {
+ ServiceTrackerOptions() = default;
+ ServiceTrackerOptions(ServiceTrackerOptions &&rhs) noexcept = default;
+ ServiceTrackerOptions(const ServiceTrackerOptions &rhs) = default;
+ ServiceTrackerOptions& operator=(ServiceTrackerOptions &&rhs) = default;
+ ServiceTrackerOptions& operator=(const ServiceTrackerOptions &rhs) = default;
+
+ std::string filter{};
+
+ /*TODO maybe refactor all I* to std::shared_ptr and use a custom deleter to sync whether a bundle is done using
+ all the functions -> i.e. safe delete and possible lock free? Not sure, because a std::shared_ptr instance
+ access it not thread safe?. Investigate */
+
+ std::function<void(I *svc)> set = {};
+ std::function<void(I *svc, const celix::Properties &props)> setWithProperties = {};
+ std::function<void(I *svc, const celix::Properties &props, const celix::IResourceBundle &owner)> setWithOwner = {};
+
+ std::function<void(I *svc)> add = {};
+ std::function<void(I *svc, const celix::Properties &props)> addWithProperties = {};
+ std::function<void(I *svc, const celix::Properties &props, const celix::IResourceBundle &owner)> addWithOwner = {};
+
+ std::function<void(I *svc)> remove = {};
+ std::function<void(I *svc, const celix::Properties &props)> removeWithProperties = {};
+ std::function<void(I *svc, const celix::Properties &props, const celix::IResourceBundle &owner)> removeWithOwner = {};
+
+ std::function<void(std::vector<I*> rankedServices)> update = {};
+ std::function<void(std::vector<std::tuple<I*, const celix::Properties*>> rankedServices)> updateWithProperties = {};
+ std::function<void(std::vector<std::tuple<I*, const celix::Properties*, const celix::IResourceBundle *>> rankedServices)> updateWithOwner = {};
+ };
+
+ //RAII service tracker: out of scope -> stop tracker
+ // NOTE access not thread safe -> TODO make thread save?
+ class ServiceTracker {
+ public:
+ ServiceTracker();
+ ServiceTracker(ServiceTracker &&rhs) noexcept;
+ ServiceTracker(const ServiceTracker &rhs) = delete;
+ ~ServiceTracker();
+ ServiceTracker& operator=(ServiceTracker &&rhs) noexcept;
+ ServiceTracker& operator=(const ServiceTracker &rhs) = delete;
+
+ int trackCount() const;
+ const std::string& serviceName() const;
+ const std::string& filter() const;
+ bool valid() const;
+
+ //TODO useService(s) calls
+
+ void stop();
+ private:
+ class Impl; //opaque impl class
+ std::unique_ptr<celix::ServiceTracker::Impl> pimpl;
+ explicit ServiceTracker(celix::ServiceTracker::Impl *impl);
+ friend ServiceRegistry;
+ };
+
+ //NOTE access thread safe
+ class ServiceRegistry {
+ public:
+ explicit ServiceRegistry(std::string name);
+ ~ServiceRegistry();
+ ServiceRegistry(celix::ServiceRegistry &&rhs) noexcept;
+ ServiceRegistry& operator=(celix::ServiceRegistry&& rhs);
+ ServiceRegistry& operator=(ServiceRegistry &rhs) = delete;
+ ServiceRegistry(const ServiceRegistry &rhs) = delete;
+
+ 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));
+ }
+
+ template<typename I>
+ celix::ServiceRegistration registerService(std::shared_ptr<I> svc, celix::Properties props = {}, std::shared_ptr<const celix::IResourceBundle> owner = {}) {
+ //TOOD refactor to using a service factory to store the shared or unique_ptr
+ auto svcName = celix::serviceName<I>();
+ return registerService(svcName, static_cast<std::shared_ptr<void>>(svc), std::move(props), std::move(owner));
+ }
+
+ template<typename F>
+ celix::ServiceRegistration registerFunctionService(const std::string &functionName, F&& func, celix::Properties props = {}, std::shared_ptr<const celix::IResourceBundle> owner = {});
+
+ template<typename I>
+ celix::ServiceRegistration registerServiceFactory(std::shared_ptr<celix::IServiceFactory<I>> factory, celix::Properties props = {}, std::shared_ptr<const celix::IResourceBundle> owner = {});
+
+ template<typename I>
+ //NOTE C++17 typename std::enable_if<!std::is_callable<I>::value, long>::type
+ long findService(const std::string &filter = "") const {
+ auto services = findServices<I>(filter);
+ return services.size() > 0 ? services[0] : -1L;
+ }
+
+ template<typename I>
+ //NOTE C++17 typename std::enable_if<std::is_callable<I>::value, long>::type
+ long findFunctionService(const std::string &functionName, const std::string &filter = "") const {
+ auto services = functionServiceName<I>(functionName, filter);
+ return services.size() > 0 ? services[0] : -1L;
+ }
+
+ template<typename I>
+ //NOTE C++17 typename std::enable_if<!std::is_callable<I>::value, std::vector<long>>::type
+ std::vector<long> findServices(const std::string &filter = "") const {
+ auto svcName = celix::serviceName<I>();
+ return findServices(svcName, filter);
+ }
+
+ template<typename F>
+ //NOTE C++17 typename std::enable_if<std::is_callable<I>::value, std::vector<long>>::type
+ std::vector<long> findFunctionServices(const std::string &functionName, const std::string &filter = "") const {
+ auto svcName = celix::functionServiceName<F>(functionName);
+ return findServices(svcName, filter);
+ }
+
+ template<typename I>
+ celix::ServiceTracker trackServices(celix::ServiceTrackerOptions<I> options = {}, std::shared_ptr<const celix::IResourceBundle> requester = {}) {
+ auto svcName = celix::serviceName<I>();
+ return trackServices<I>(svcName, std::move(options), std::move(requester));
+ }
+
+ template<typename F>
+ celix::ServiceTracker trackFunctionServices(std::string functionName, celix::ServiceTrackerOptions<F> options = {}, std::shared_ptr<const celix::IResourceBundle> requester = {}) {
+ auto svcName = celix::functionServiceName<F>(functionName);
+ return trackServices<F>(svcName, std::move(options), std::move(requester));
+ }
+
+ //TODO trackTrackers
+
+ template<typename I>
+ int useServices(std::function<void(I& svc)> use, const std::string &filter = "", std::shared_ptr<const celix::IResourceBundle> requester = {}) const {
+ auto svcName = celix::serviceName<I>();
+ return useServices<I>(svcName, use, nullptr, nullptr, filter, std::move(requester));
+ }
+
+ template<typename I>
+ int useServices(std::function<void(I& svc, const celix::Properties &props)> use, const std::string &filter = "", std::shared_ptr<const celix::IResourceBundle> requester = {}) const {
+ auto svcName = celix::serviceName<I>();
+ return useServices<I>(svcName, nullptr, use, nullptr, filter, std::move(requester));
+ }
+
+ template<typename I>
+ int useServices(std::function<void(I& svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> use, const std::string &filter = "", std::shared_ptr<const celix::IResourceBundle> requester = {}) const {
+ auto svcName = celix::serviceName<I>();
+ return useServices<I>(svcName, nullptr, nullptr, use, filter, std::move(requester));
+ }
+
+ template<typename F>
+ int useFunctionServices(const std::string &functionName, std::function<void(F &function)> use, const std::string &filter = "", std::shared_ptr<const celix::IResourceBundle> requester = {}) const {
+ auto svcName = celix::functionServiceName<F>(functionName);
+ return useServices<F>(svcName, use, nullptr, nullptr, filter, std::move(requester));
+ }
+
+ template<typename F>
+ int useFunctionServices(const std::string &functionName, std::function<void(F &function, const celix::Properties&)> use, const std::string &filter = "", std::shared_ptr<const celix::IResourceBundle> requester = {}) const {
+ auto svcName = celix::functionServiceName<F>(functionName);
+ return useServices<F>(svcName, nullptr, use, nullptr, filter, std::move(requester));
+ }
+
+ template<typename F>
+ int useFunctionServices(const std::string &functionName, std::function<void(F &function, const celix::Properties&, const celix::IResourceBundle&)> use, const std::string &filter = "", std::shared_ptr<const celix::IResourceBundle> requester = {}) const {
+ auto svcName = celix::functionServiceName<F>(functionName);
+ return useServices<F>(svcName, nullptr, nullptr, use, filter, std::move(requester));
+ }
+
+ template<typename I>
+ bool useService(std::function<void(I& svc)> use, const std::string &filter = "", std::shared_ptr<const celix::IResourceBundle> requester = {}) const {
+ auto svcName = celix::serviceName<I>();
+ return useService<I>(svcName, use, nullptr, nullptr, filter, std::move(requester));
+ }
+
+ template<typename I>
+ bool useService(std::function<void(I& svc, const celix::Properties &props)> use, const std::string &filter = "", std::shared_ptr<const celix::IResourceBundle> requester = {}) const {
+ auto svcName = celix::serviceName<I>();
+ return useService<I>(svcName, nullptr, use, nullptr, filter, std::move(requester));
+ }
+
+ template<typename I>
+ bool useService(std::function<void(I& svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> use, const std::string &filter = "", std::shared_ptr<const celix::IResourceBundle> requester = {}) const {
+ auto svcName = celix::serviceName<I>();
+ return useService<I>(svcName, nullptr, nullptr, use, filter, std::move(requester));
+ }
+
+ template<typename F>
+ int useFunctionService(const std::string &functionName, std::function<void(F &function)> use, const std::string &filter = "", std::shared_ptr<const celix::IResourceBundle> requester = {}) const {
+ auto svcName = celix::functionServiceName<F>(functionName);
+ return useService<F>(svcName, use, nullptr, nullptr, filter, std::move(requester));
+ }
+
+ template<typename F>
+ int useFunctionService(const std::string &functionName, std::function<void(F &function, const celix::Properties&)> use, const std::string &filter = "", std::shared_ptr<const celix::IResourceBundle> requester = {}) const {
+ auto svcName = celix::functionServiceName<F>(functionName);
+ return useService<F>(svcName, nullptr, use, nullptr, filter, std::move(requester));
+ }
+
+ template<typename F>
+ int useFunctionService(const std::string &functionName, std::function<void(F &function, const celix::Properties&, const celix::IResourceBundle&)> use, const std::string &filter = "", std::shared_ptr<const celix::IResourceBundle> requester = {}) const {
+ auto svcName = celix::functionServiceName<F>(functionName);
+ return useService<F>(svcName, nullptr, nullptr, use, filter, std::move(requester));
+ }
+
+
+ long nrOfRegisteredServices() const;
+ long nrOfServiceTrackers() const;
+ private:
+ class Impl;
+ std::unique_ptr<celix::ServiceRegistry::Impl> pimpl;
+
+ //register services
+ celix::ServiceRegistration registerService(std::string svcName, std::shared_ptr<void> svc, celix::Properties props, std::shared_ptr<const celix::IResourceBundle> owner);
+ celix::ServiceRegistration registerServiceFactory(std::string svcName, std::shared_ptr<celix::IServiceFactory<void>> factory, celix::Properties props, std::shared_ptr<const celix::IResourceBundle> owner);
+
+ //use Services
+ template<typename I>
+ int useServices(
+ const std::string &svcName,
+ std::function<void(I &svc)> use,
+ std::function<void(I &svc, const celix::Properties &props)> useWithProps,
+ std::function<void(I &svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> useWithOwner,
+ const std::string &filter,
+ std::shared_ptr<const celix::IResourceBundle> requester) const;
+ int useServices(const std::string &svcName, std::function<void(void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> &use, const std::string &filter, std::shared_ptr<const celix::IResourceBundle> requester) const;
+
+ template<typename I>
+ bool useService(
+ const std::string &svcName,
+ std::function<void(I &svc)> use,
+ std::function<void(I &svc, const celix::Properties &props)> useWithProps,
+ std::function<void(I &svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> useWithOwner,
+ const std::string &filter,
+ std::shared_ptr<const celix::IResourceBundle> requester) const;
+ bool useService(const std::string &svcName, std::function<void(void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> &use, const std::string &filter, std::shared_ptr<const celix::IResourceBundle> requester) const;
+
+ //find Services
+ std::vector<long> findServices(const std::string &name, const std::string &filter) const;
+
+
+ //track services
+ template<typename I>
+ celix::ServiceTracker trackServices(std::string svcName, celix::ServiceTrackerOptions<I> options, std::shared_ptr<const celix::IResourceBundle> requester);
+ celix::ServiceTracker trackServices(std::string svcName, ServiceTrackerOptions<void> options, std::shared_ptr<const celix::IResourceBundle> requester);
+ };
+}
+
+
+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> {
+ public:
+ FunctionServiceFactory(F&& _function) : function{std::forward<F>(_function)} {}
+
+ void* getService(const celix::IResourceBundle &, const celix::Properties &) noexcept override {
+ return static_cast<void*>(&function);
+ }
+ void ungetService(const celix::IResourceBundle &, const celix::Properties &) noexcept override {
+ //nop;
+ }
+ private:
+ F function;
+ };
+ auto factory = std::shared_ptr<celix::IServiceFactory<void>>{new FunctionServiceFactory{std::forward<F>(func)}};
+
+ std::string svcName = celix::functionServiceName<typename std::remove_reference<F>::type>(functionName);
+
+ return registerServiceFactory(std::move(svcName), factory, std::move(props), std::move(owner));
+}
+
+template<typename I>
+inline celix::ServiceRegistration celix::ServiceRegistry::registerServiceFactory(std::shared_ptr<celix::IServiceFactory<I>> factory, celix::Properties props, std::shared_ptr<const celix::IResourceBundle> owner) {
+ std::string svcName = celix::serviceName<I>();
+
+ class VoidServiceFactory : public celix::IServiceFactory<void> {
+ public:
+ VoidServiceFactory(std::shared_ptr<celix::IServiceFactory<I>> _factory) : factory{std::move(_factory)} {}
+
+ void* getService(const celix::IResourceBundle &bnd, const celix::Properties &props) noexcept override {
+ I* service = factory->getService(bnd, props);
+ return static_cast<void*>(service);
+ }
+ void ungetService(const celix::IResourceBundle &bnd, const celix::Properties &props) noexcept override {
+ factory->ungetService(bnd, props);
+ }
+ private:
+ std::shared_ptr<celix::IServiceFactory<I>> factory;
+ };
+
+ auto voidFactory = std::shared_ptr<celix::IServiceFactory<void>>{new VoidServiceFactory{std::move(factory)}};
+ return registerServiceFactory(std::move(svcName), std::move(voidFactory), std::move(props), std::move(owner));
+}
+
+template<typename I>
+inline int celix::ServiceRegistry::useServices(
+ const std::string &svcName,
+ std::function<void(I &svc)> use,
+ std::function<void(I &svc, const celix::Properties &props)> useWithProps,
+ std::function<void(I &svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> useWithOwner,
+ const std::string &filter,
+ std::shared_ptr<const celix::IResourceBundle> requester) const {
+ std::function<void(void*,const celix::Properties&, const celix::IResourceBundle&)> voidUse = [&](void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd) {
+ I* typedSvc = static_cast<I*>(svc);
+ if (use) {
+ use(*typedSvc);
+ }
+ if (useWithProps) {
+ useWithProps(*typedSvc, props);
+ }
+ if (useWithOwner) {
+ useWithOwner(*typedSvc, props, bnd);
+ }
+ };
+ return useServices(svcName, voidUse, filter, requester);
+}
+
+template<typename I>
+inline bool celix::ServiceRegistry::useService(
+ const std::string &svcName,
+ std::function<void(I &svc)> use,
+ std::function<void(I &svc, const celix::Properties &props)> useWithProps,
+ std::function<void(I &svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> useWithOwner,
+ const std::string &filter,
+ std::shared_ptr<const celix::IResourceBundle> requester) const {
+ std::function<void(void*,const celix::Properties&, const celix::IResourceBundle&)> voidUse = [&](void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd) -> void {
+ I* typedSvc = static_cast<I*>(svc);
+ if (use) {
+ use(*typedSvc);
+ }
+ if (useWithProps) {
+ useWithProps(*typedSvc, props);
+ }
+ if (useWithOwner) {
+ useWithOwner(*typedSvc, props, bnd);
+ }
+ };
+ return useService(svcName, voidUse, filter, requester);
+}
+
+template<typename I>
+inline celix::ServiceTracker celix::ServiceRegistry::trackServices(std::string svcName, const celix::ServiceTrackerOptions<I> options, std::shared_ptr<const celix::IResourceBundle> requester) {
+ ServiceTrackerOptions<void> opts{};
+ opts.filter = std::move(options.filter);
+
+ if (options.set != nullptr) {
+ auto set = std::move(options.set);
+ opts.set = [set](void *svc){
+ I *typedSvc = static_cast<I*>(svc);
+ set(typedSvc);
+ };
+ }
+ if (options.setWithProperties != nullptr) {
+ auto set = std::move(options.setWithProperties);
+ opts.setWithProperties = [set](void *svc, const celix::Properties &props){
+ I *typedSvc = static_cast<I*>(svc);
+ set(typedSvc, props);
+ };
+ }
+ if (options.setWithOwner != nullptr) {
+ auto set = std::move(options.setWithOwner);
+ opts.setWithOwner = [set](void *svc, const celix::Properties &props, const celix::IResourceBundle &owner){
+ I *typedSvc = static_cast<I*>(svc);
+ set(typedSvc, props, owner);
+ };
+ }
+
+ if (options.add != nullptr) {
+ auto add = std::move(options.add);
+ opts.add = [add](void *svc) {
+ I *typedSvc = static_cast<I*>(svc); //note actual argument is I*
+ add(typedSvc);
+ };
+ }
+ if (options.addWithProperties != nullptr) {
+ auto add = std::move(options.addWithProperties);
+ opts.addWithProperties = [add](void *svc, const celix::Properties &props) {
+ I *typedSvc = static_cast<I*>(svc); //note actual argument is I*
+ add(typedSvc, props);
+ };
+ }
+ if (options.addWithOwner != nullptr) {
+ auto add = std::move(options.addWithOwner);
+ opts.addWithOwner = [add](void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd) {
+ I *typedSvc = static_cast<I*>(svc); //note actual argument is I*
+ add(typedSvc, props, bnd);
+ };
+ }
+
+ if (options.remove != nullptr) {
+ auto rem = std::move(options.remove);
+ opts.remove = [rem](void *svc) {
+ I *typedSvc = static_cast<I*>(svc); //note actual argument is I*
+ rem(typedSvc);
+ };
+ }
+ if (options.removeWithProperties != nullptr) {
+ auto rem = std::move(options.removeWithProperties);
+ opts.removeWithProperties = [rem](void *svc, const celix::Properties &props) {
+ I *typedSvc = static_cast<I*>(svc); //note actual argument is I*
+ rem(typedSvc, props);
+ };
+ }
+ if (options.removeWithOwner != nullptr) {
+ auto rem = std::move(options.removeWithOwner);
+ opts.removeWithOwner = [rem](void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd) {
+ I *typedSvc = static_cast<I*>(svc); //note actual argument is I*
+ rem(typedSvc, props, bnd);
+ };
+ }
+
+ if (options.update != nullptr) {
+ auto update = std::move(options.update);
+ opts.update = [update](std::vector<void*> rankedServices) {
+ std::vector<I*> typedServices{};
+ typedServices.reserve(rankedServices.size());
+ for (void *svc : rankedServices) {
+ typedServices.push_back(static_cast<I*>(svc));
+ }
+ update(std::move(typedServices));
+ };
+ }
+ if (options.updateWithProperties != nullptr) {
+ auto update = std::move(options.updateWithProperties);
+ opts.updateWithProperties = [update](std::vector<std::tuple<void*, const celix::Properties *>> rankedServices) {
+ std::vector<std::tuple<I*, const celix::Properties*>> typedServices{};
+ typedServices.reserve(rankedServices.size());
+ for (auto &tuple : rankedServices) {
+ typedServices.push_back(std::make_tuple(static_cast<I*>(std::get<0>(tuple)), std::get<1>(tuple)));
+ }
+ update(std::move(typedServices));
+ };
+ }
+ if (options.updateWithOwner != nullptr) {
+ auto update = std::move(options.updateWithOwner);
+ opts.updateWithOwner = [update](std::vector<std::tuple<void*, const celix::Properties *, const celix::IResourceBundle*>> rankedServices) {
+ std::vector<std::tuple<I*, const celix::Properties*, const celix::IResourceBundle*>> typedServices{};
+ typedServices.reserve(rankedServices.size());
+ for (auto &tuple : rankedServices) {
+ typedServices.push_back(std::make_tuple(static_cast<I*>(std::get<0>(tuple)), std::get<1>(tuple), std::get<2>(tuple)));
+ }
+ update(std::move(typedServices));
+ };
+ }
+
+ return trackServices(std::move(svcName), std::move(opts), requester);
+}
+
+#endif //CXX_CELIX_SERVICEREGISTRY_H
diff --git a/libs/registry/include/celix/Utils.h b/libs/registry/include/celix/Utils.h
new file mode 100644
index 0000000..ae61cd7
--- /dev/null
+++ b/libs/registry/include/celix/Utils.h
@@ -0,0 +1,102 @@
+/**
+ *Licensed to the Apache Software Foundation (ASF) under one
+ *or more contributor license agreements. See the NOTICE file
+ *distributed with this work for additional information
+ *regarding copyright ownership. The ASF licenses this file
+ *to you under the Apache License, Version 2.0 (the
+ *"License"); you may not use this file except in compliance
+ *with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *Unless required by applicable law or agreed to in writing,
+ *software distributed under the License is distributed on an
+ *"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ *specific language governing permissions and limitations
+ *under the License.
+ */
+
+#ifndef CXX_CELIX_UTILS_H
+#define CXX_CELIX_UTILS_H
+
+
+#include <string>
+#include <iostream>
+
+namespace {
+
+ template<typename INTERFACE_TYPENAME>
+ std::string typeName() {
+ std::string result;
+
+ const char *templateStr = "INTERFACE_TYPENAME = ";
+ const size_t templateStrLen = strlen(templateStr);
+
+ result = __PRETTY_FUNCTION__; //USING pretty function to retrieve the filled in template argument without using typeid()
+ size_t bpos = result.find(templateStr) + templateStrLen; //find begin pos after INTERFACE_TYPENAME = entry
+ size_t epos = bpos;
+ while (isalnum(result[epos]) || result[epos] == '_' || result[epos] == ':' || result[epos] == ' ' || result[epos] == '*' || result[epos] == '&' || result[epos] == '<' || result[epos] == '>') {
+ epos += 1;
+ }
+ size_t len = epos - bpos;
+ result = result.substr(bpos, len);
+
+ if (result.empty()) {
+ std::cerr << "Cannot infer type name in function call '" << __PRETTY_FUNCTION__ << "'\n'";
+ }
+
+ return result;
+ }
+
+ template<typename Arg>
+ std::string argName() {
+ return typeName<Arg>(); //terminal;
+ }
+
+ template<typename Arg1, typename Arg2, typename... Args>
+ std::string argName() {
+ return typeName<Arg1>() + ", " + argName<Arg2, Args...>();
+ }
+
+ template<typename R>
+ std::string functionName(const std::string &funcName) {
+ return funcName + " [std::function<" + typeName<R>() + "()>]";
+ }
+
+ template<typename R, typename Arg1, typename... Args>
+ std::string functionName(const std::string &funcName) {
+ return funcName + " [std::function<" + typeName<R>() + "(" + argName<Arg1, Args...>() + ")>]";
+ }
+};
+
+namespace celix {
+
+ /* TODO
+ template<typename I>
+ typename std::enable_if<I::FQN, std::string>::type
+ serviceName() {
+ return I::FQN;
+ }*/
+
+ /**
+ * Returns the service name for a type I
+ */
+ template<typename I>
+ //NOTE C++17 typename std::enable_if<!std::is_callable<I>::value, std::string>::type
+ std::string serviceName() {
+ return typeName<I>();
+ }
+
+ /**
+ * Returns the service name for a std::function I.
+ * Note that for a std::function the additional function name is needed to get a fully qualified service name;
+ */
+ template<typename F>
+ //NOTE C++17 typename std::enable_if<std::is_callable<I>::value, std::string>::type
+ std::string functionServiceName(const std::string &fName) {
+ return functionName<decltype(&F::operator())>(fName);
+ }
+}
+
+#endif //CXX_CELIX_UTILS_H
diff --git a/libs/registry/src/Filter.cc b/libs/registry/src/Filter.cc
new file mode 100644
index 0000000..b92cf32
--- /dev/null
+++ b/libs/registry/src/Filter.cc
@@ -0,0 +1,464 @@
+/**
+ *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 "celix/Filter.h"
+
+#include <glog/logging.h>
+
+static celix::FilterCriteria* filter_parseFilter(const char *filterString, int *pos);
+static void filter_skipWhiteSpace(const char *filterString, int *pos);
+static celix::FilterCriteria* filter_parseAndOrOr(const char *filterString, celix::FilterOperator andOrOr, int *pos);
+static celix::FilterCriteria* filter_parseFilterComp(const char *filterString, int *pos);
+static celix::FilterCriteria* filter_parseItem(const char *filterString, int *pos);
+static std::string filter_parseAttr(const char *filterString, int *pos);
+static std::string filter_parseValue(const char *filterString, int *pos);
+
+static void filter_skipWhiteSpace(const char *filterString, int *pos) {
+ size_t length;
+ for (length = strlen(filterString); (*pos < (int)(length)) && isspace(filterString[*pos]);) {
+ (*pos)++;
+ }
+}
+
+static celix::FilterCriteria* filter_parseAndOrOr(const char *filterString, celix::FilterOperator andOrOr, int *pos) {
+
+ filter_skipWhiteSpace(filterString, pos);
+ bool failure = false;
+
+ if (filterString[*pos] != '(') {
+ LOG(ERROR) << "Filter Error: Missing '('.\n";
+ return nullptr;
+ }
+
+ std::vector<std::shared_ptr<celix::FilterCriteria>> subcriteria{};
+ while (filterString[*pos] == '(') {
+ celix::FilterCriteria *cr = filter_parseFilter(filterString, pos);
+ if (cr == nullptr) {
+ failure = true;
+ break;
+ }
+ subcriteria.push_back(std::unique_ptr<celix::FilterCriteria>{cr});
+ }
+
+
+ celix::FilterCriteria* criteria = nullptr;
+ if (!failure) {
+ criteria = new celix::FilterCriteria{
+ .attribute = "",
+ .op = andOrOr,
+ .value = "",
+ .subcriteria = std::move(subcriteria)
+ };
+ }
+
+ return criteria;
+}
+
+static celix::FilterCriteria* filter_parseNot(const char * filterString, int * pos) {
+ celix::FilterCriteria *criteria = nullptr;
+
+ filter_skipWhiteSpace(filterString, pos);
+ if (filterString[*pos] != '(') {
+ LOG(ERROR) << "Filter Error: Missing '('.\n";
+ return nullptr;
+ }
+
+ std::vector<std::shared_ptr<celix::FilterCriteria>> subcriteria{};
+ celix::FilterCriteria *cr = filter_parseFilter(filterString, pos);
+ if (cr != nullptr) {
+ subcriteria.push_back(std::unique_ptr<celix::FilterCriteria>(cr));
+ criteria = new celix::FilterCriteria {
+ .attribute = "",
+ .op = celix::FilterOperator::NOT,
+ .value = "",
+ .subcriteria = std::move(subcriteria)
+ };
+ }
+
+ return criteria;
+}
+
+static std::string filter_parseAttr(const char *filterString, int *pos) {
+ std::string attr{};
+ char c;
+ int begin = *pos;
+ int end = *pos;
+ int length = 0;
+
+ filter_skipWhiteSpace(filterString, pos);
+ c = filterString[*pos];
+
+ while (c != '~' && c != '<' && c != '>' && c != '=' && c != '(' && c != ')') {
+ (*pos)++;
+
+ if (!isspace(c)) {
+ end = *pos;
+ }
+
+ c = filterString[*pos];
+ }
+
+ length = end - begin;
+
+ if (length == 0) {
+ LOG(ERROR) << "Filter Error: Missing attr.\n";
+ } else {
+ attr.assign(filterString+begin, (size_t)length);
+ }
+
+ return attr;
+}
+
+static std::string filter_parseValue(const char *filterString, int *pos) {
+ std::string val{};
+ int keepRunning = 1;
+
+ while (keepRunning) {
+ char c = filterString[*pos];
+
+ switch (c) {
+ case ')': {
+ keepRunning = 0;
+ break;
+ }
+ case '(': {
+ LOG(ERROR) << "Filter Error: Invalid value.\n";
+ val = "";
+ keepRunning = 0;
+ break;
+ }
+ case '\0':{
+ LOG(ERROR) << "Filter Error: Unclosed bracket.\n";
+ val = "";
+ keepRunning = 0;
+ break;
+ }
+ case '\\': {
+ (*pos)++;
+ c = filterString[*pos];
+ char ch[2];
+ ch[0] = c;
+ ch[1] = '\0';
+ val.append(ch);
+ (*pos)++;
+ break;
+ }
+ default: {
+ char ch[2];
+ ch[0] = c;
+ ch[1] = '\0';
+ val.append(ch);
+ (*pos)++;
+ break;
+ }
+ }
+ }
+
+ if (val.empty()) {
+ LOG(ERROR) << "Filter Error: Missing value.\n";
+ }
+ return val;
+}
+
+static celix::FilterCriteria* filter_parseItem(const char * filterString, int * pos) {
+ celix::FilterCriteria* criteria = nullptr;
+
+ std::string attr = filter_parseAttr(filterString, pos);
+ if (attr.empty()) {
+ return nullptr;
+ }
+
+ filter_skipWhiteSpace(filterString, pos);
+ switch(filterString[*pos]) {
+ case '~': {
+ if (filterString[*pos + 1] == '=') {
+ *pos += 2; //skip ~=
+ std::string val = filter_parseValue(filterString, pos);
+ if (val.empty()) {
+ LOG(ERROR) << "Unexpected emtpy value after ~= operator in filter '" << filterString << "'\n";
+ } else {
+ criteria = new celix::FilterCriteria{
+ .attribute = std::move(attr),
+ .op = celix::FilterOperator::APPROX,
+ .value = std::move(val),
+ .subcriteria = {}
+ };
+ }
+ } else {
+ LOG(ERROR) << "Unexpected ~ char without the expected = in filter '" << filterString << "'\n";
+ }
+ break;
+ }
+ case '>': {
+ auto op = celix::FilterOperator::GREATER;
+ if (filterString[*pos + 1] == '=') {
+ *pos += 2; //skip >=
+ op = celix::FilterOperator::GREATER_EQUAL;
+ } else {
+ *pos += 1; //skip >
+ }
+ std::string val = filter_parseValue(filterString, pos);
+ if (val.empty()) {
+ LOG(ERROR) << "Unexpected empty value in > or >= operator for filter '" << filterString << "'\n";
+ } else {
+ criteria = new celix::FilterCriteria{
+ .attribute = std::move(attr),
+ .op = op,
+ .value = std::move(val),
+ .subcriteria = {}
+ };
+ }
+ break;
+ }
+ case '<': {
+ celix::FilterOperator op = celix::FilterOperator::LESS;
+ if (filterString[*pos + 1] == '=') {
+ *pos += 2; //skip <=
+ op = celix::FilterOperator::LESS_EQUAL;
+ } else {
+ *pos += 1; //skip <
+ }
+ std::string val = filter_parseValue(filterString, pos);
+ if (val.empty()) {
+ LOG(ERROR) << "Unexpected emtpy value after < or <= operator in filter '" << filterString <<"'\n";
+ } else {
+ criteria = new celix::FilterCriteria {
+ .attribute = std::move(attr),
+ .op = op,
+ .value = std::move(val),
+ .subcriteria = {}
+ };
+ }
+ break;
+ }
+ case '=': {
+ if (filterString[*pos + 1] == '*') {
+ int oldPos = *pos;
+ *pos += 2; //skip =*
+ filter_skipWhiteSpace(filterString, pos);
+ if (filterString[*pos] == ')') {
+ criteria = new celix::FilterCriteria{
+ .attribute = attr,
+ .op = celix::FilterOperator::PRESENT,
+ .value = "",
+ .subcriteria = {}
+ };
+ } else {
+ *pos = oldPos; //no present criteria -> reset
+ }
+ }
+ if (criteria == nullptr) { //i.e. no present criteria set
+ //note that the value can start and end with a wildchar *. this is not parsed specifically, but tested
+ *pos += 1; //skip =
+ filter_skipWhiteSpace(filterString, pos);
+ std::string val = filter_parseValue(filterString, pos);
+ bool substring = val.size() > 2 && (val.front() == '*' || val.back() == '*');
+ if (val.empty()) {
+ LOG(ERROR) << "Unexpected emtpy value after = operator in filter '" << filterString << "'\n";
+ } else {
+ criteria = new celix::FilterCriteria{
+ .attribute = std::move(attr),
+ .op = substring ? celix::FilterOperator::SUBSTRING : celix::FilterOperator::EQUAL,
+ .value = std::move(val),
+ .subcriteria = {}
+ };
+ }
+ }
+ break;
+ }
+ default: {
+ LOG(ERROR) << "Unexpected operator " << *pos << std::endl;
+ break;
+ }
+ }
+
+ if (criteria == nullptr) {
+ LOG(ERROR) << "Filter Error: Invalid operator.\n";
+ }
+
+ return criteria;
+}
+
+static celix::FilterCriteria* filter_parseFilterComp(const char *filterString, int *pos) {
+ celix::FilterCriteria* criteria = nullptr;
+ char c;
+ filter_skipWhiteSpace(filterString, pos);
+
+ c = filterString[*pos];
+
+ switch (c) {
+ case '&': {
+ (*pos)++;
+ criteria = filter_parseAndOrOr(filterString, celix::FilterOperator::AND, pos);
+ break;
+ }
+ case '|': {
+ (*pos)++;
+ criteria = filter_parseAndOrOr(filterString, celix::FilterOperator::OR, pos);
+ break;
+ }
+ case '!': {
+ (*pos)++;
+ criteria = filter_parseNot(filterString, pos);
+ break;
+ }
+ default : {
+ criteria =filter_parseItem(filterString, pos);
+ break;
+ }
+ }
+ return criteria;
+}
+
+
+static celix::FilterCriteria* filter_parseFilter(const char *filterString, int *pos) {
+ celix::FilterCriteria *criteria = nullptr;
+ filter_skipWhiteSpace(filterString, pos);
+ if (filterString[*pos] != '(') {
+ LOG(ERROR) << "Filter Error: Missing '(' in filter string '" << filterString << "'.\n";
+ return NULL;
+ }
+ (*pos)++; //consume (
+
+ criteria = filter_parseFilterComp(filterString, pos);
+
+ filter_skipWhiteSpace(filterString, pos);
+
+ if (filterString[*pos] != ')') {
+ LOG(ERROR) << "Filter Error: Missing ')' in filter string '" << filterString << "'.\n";
+ if (criteria != NULL) {
+ delete criteria;
+ }
+ return NULL;
+ }
+ (*pos)++;
+ filter_skipWhiteSpace(filterString, pos);
+
+ return criteria;
+}
+
+celix::FilterCriteria* celix::Filter::parseFilter() {
+ celix::FilterCriteria* criteria = nullptr;
+ if (!filterStr.empty()) {
+ int pos = 0;
+ criteria = filter_parseFilter(filterStr.c_str(), &pos);
+ if (criteria != nullptr && pos != (int) filterStr.size()) {
+ LOG(ERROR) << "Filter Error: Missing '(' in filter string '" << filterStr << "'.'n";
+ delete criteria;
+ criteria = nullptr;
+ }
+ }
+ return criteria;
+}
+
+static bool match_criteria(const celix::Properties &props, const celix::FilterCriteria &criteria) {
+ bool result;
+ switch (criteria.op) {
+ case celix::FilterOperator::AND: {
+ result = true;
+ for (const auto &sc : criteria.subcriteria) {
+ if (!match_criteria(props, *sc)) {
+ result = false;
+ break;
+ }
+ }
+ break;
+ }
+ case celix::FilterOperator::OR: {
+ result = false;
+ for (const auto &sc : criteria.subcriteria) {
+ if (match_criteria(props, *sc)) {
+ result = true;
+ break;
+ }
+ }
+ break;
+ }
+ case celix::FilterOperator::NOT: {
+ result = !match_criteria(props, *criteria.subcriteria[0]);
+ break;
+ }
+ case celix::FilterOperator::SUBSTRING: {
+ const std::string &val = celix::getProperty(props, criteria.attribute, "");
+ if (val.empty()) {
+ result = false;
+ } else {
+ bool wildcharAtFront = criteria.value.front() == '*';
+ bool wildcharAtBack = criteria.value.back() == '*';
+ std::string needle = wildcharAtFront ? criteria.value.substr(1) : criteria.value;
+ needle = wildcharAtBack ? needle.substr(0, needle.size() - 1) : needle;
+ const char *ptr = strstr(val.c_str(), needle.c_str());
+ if (wildcharAtFront && wildcharAtBack) {
+ result = ptr != nullptr;
+ } else if (wildcharAtFront) { //-> end must match
+ result = ptr != nullptr && ptr + strlen(ptr) == val.c_str() + strlen(val.c_str());
+ } else { //wildCharAtBack -> begin must match
+ result = ptr != nullptr && ptr == val.c_str();
+ }
+ }
+ break;
+ }
+ case celix::FilterOperator::EQUAL: {
+ const std::string &val = celix::getProperty(props, criteria.attribute, "");
+ result = !val.empty() && val == criteria.value;
+ break;
+ }
+ case celix::FilterOperator::GREATER_EQUAL: {
+ const std::string &val = celix::getProperty(props, criteria.attribute, "");
+ result = !val.empty() && val >= criteria.value;
+ break;
+ }
+ case celix::FilterOperator::LESS: {
+ const std::string &val = celix::getProperty(props, criteria.attribute, "");
+ result = !val.empty() && val < criteria.value;
+ break;
+ }
+ case celix::FilterOperator::LESS_EQUAL: {
+ const std::string &val = celix::getProperty(props, criteria.attribute, "");
+ result = !val.empty() && val <= criteria.value;
+ break;
+ }
+ case celix::FilterOperator::PRESENT: {
+ const std::string &val = celix::getProperty(props, criteria.attribute, "");
+ result = !val.empty();
+ break;
+ }
+ case celix::FilterOperator::APPROX: {
+ const std::string &val = celix::getProperty(props, criteria.attribute, "");
+ result = !val.empty() && strcasecmp(val.c_str(), criteria.value.c_str()) == 0;
+ break;
+ }
+ default: {
+ result = false;
+ break;
+ }
+ }
+
+ return result;
+}
+
+bool celix::Filter::match(const celix::Properties &props) const {
+ if (!valid()) {
+ return false;
+ } else if (filterStr.empty()) {
+ return true;
+ }
+
+ return match_criteria(props, *criteria);
+}
\ No newline at end of file
diff --git a/libs/registry/src/ServiceRegistry.cc b/libs/registry/src/ServiceRegistry.cc
new file mode 100644
index 0000000..862013f
--- /dev/null
+++ b/libs/registry/src/ServiceRegistry.cc
@@ -0,0 +1,716 @@
+#include <utility>
+
+/**
+ *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 <unordered_map>
+#include <mutex>
+#include <set>
+#include <utility>
+#include <thread>
+
+#include <glog/logging.h>
+
+#include "celix/Constants.h"
+#include "celix/ServiceRegistry.h"
+#include "celix/Filter.h"
+
+static std::string emptyString{};
+
+namespace {
+
+ class EmptyBundle : public celix::IResourceBundle {
+ public:
+ ~EmptyBundle() override = default;
+
+ long id() const noexcept override { return 0; }
+
+ const std::string& root() const noexcept override {
+ static std::string empty{};
+ return empty;
+ }
+
+ bool has(const std::string&) const noexcept override { return false; }
+ bool isDir(const std::string&) const noexcept override { return false; }
+ bool isFile(const std::string&) const noexcept override { return false; }
+
+ //std::istream& open(const std::string &path) override {}
+ //std::fstream& open(const std::string &path) override {}
+ std::vector<std::string> readDir(const std::string&) const noexcept override { return std::vector<std::string>{};}
+ };
+
+ struct SvcEntry {
+ explicit SvcEntry(std::shared_ptr<const celix::IResourceBundle> _owner, long _svcId, std::string _svcName, std::shared_ptr<void> _svc, std::shared_ptr<celix::IServiceFactory<void>> _svcFac,
+ celix::Properties &&_props) :
+ owner{std::move(_owner)}, svcId{_svcId}, svcName{std::move(_svcName)},
+ props{std::forward<celix::Properties>(_props)},
+ ranking{celix::getProperty(props, celix::SERVICE_RANKING, 0L)},
+ svc{_svc}, svcFactory{_svcFac} {}
+
+ SvcEntry(SvcEntry &&rhs) = delete;
+ SvcEntry &operator=(SvcEntry &&rhs) = delete;
+ SvcEntry(const SvcEntry &rhs) = delete;
+ SvcEntry &operator=(const SvcEntry &rhs) = delete;
+
+ const std::shared_ptr<const celix::IResourceBundle> owner;
+ const long svcId;
+ const std::string svcName;
+ const celix::Properties props;
+ const long ranking;
+
+ void *service(const celix::IResourceBundle &requester) const {
+ if (svcFactory) {
+ return svcFactory->getService(requester, props);
+ } else {
+ return svc.get();
+ }
+ }
+
+ bool factory() const { return svcFactory != nullptr; }
+
+ void incrUsage() const {
+ LOG(WARNING) << "TODO use shared_ptr unique instead ?? how to sync?";
+ std::lock_guard<std::mutex> lck{mutex};
+ usage += 1;
+ }
+
+ void decrUsage() const {
+ LOG(WARNING) << "TODO use shared_ptr unique instead ?? how is sync?";
+ std::lock_guard<std::mutex> lck{mutex};
+ usage -= 1;
+ cond.notify_all();
+ }
+
+ void waitTillUnused() const {
+ std::unique_lock<std::mutex> lock{mutex};
+ cond.wait(lock, [this]{return usage == 0;});
+ }
+ private:
+ //svc, svcSharedPtr or svcFactory is set
+ const std::shared_ptr<void> svc;
+ const std::shared_ptr<celix::IServiceFactory<void>> svcFactory;
+
+
+ //sync TODO refactor to atomics
+ mutable std::mutex mutex{};
+ mutable std::condition_variable cond{};
+ mutable int usage{1};
+ };
+
+ struct SvcEntryLess {
+ bool operator()( const std::shared_ptr<const SvcEntry>& lhs, const std::shared_ptr<const SvcEntry>& rhs ) const {
+ if (lhs->ranking == rhs->ranking) {
+ return lhs->svcId < rhs->svcId;
+ } else {
+ return lhs->ranking > rhs->ranking; //note inverse -> higher rank first in set
+ }
+ }
+ };
+
+ struct SvcTrackerEntry {
+ const long id;
+ const std::shared_ptr<const celix::IResourceBundle> owner;
+ const std::string svcName;
+ const celix::ServiceTrackerOptions<void> opts;
+ const celix::Filter filter;
+
+ explicit SvcTrackerEntry(long _id, std::shared_ptr<const celix::IResourceBundle> _owner, std::string _svcName, celix::ServiceTrackerOptions<void> _opts) :
+ id{_id}, owner{std::move(_owner)}, svcName{std::move(_svcName)}, opts{std::move(_opts)}, filter{opts.filter} {}
+
+ SvcTrackerEntry(SvcTrackerEntry &&rhs) = delete;
+ SvcTrackerEntry &operator=(SvcTrackerEntry &&rhs) = delete;
+ SvcTrackerEntry(const SvcTrackerEntry &rhs) = delete;
+ SvcTrackerEntry &operator=(const SvcTrackerEntry &rhs) = delete;
+ ~SvcTrackerEntry() {}
+
+ void clear() {
+ //TODO update, make special rem (e.g. only call the use set callbacks once with a nullptr)
+ std::vector<std::shared_ptr<const SvcEntry>> removeEntries{};
+ {
+ std::lock_guard<std::mutex> lck{tracked.mutex};
+ for (auto &entry : tracked.entries) {
+ removeEntries.push_back(entry);
+ }
+ tracked.entries.clear();
+ }
+ for (auto &entry : removeEntries) {
+ remMatch(entry); //note fill try to erase entry from entries again, TODO check if this is safe
+ }
+ }
+
+ bool valid() const {
+ return !svcName.empty() && filter.valid();
+ }
+
+ bool match(const SvcEntry& entry) const {
+ //note should only called for the correct svcName
+ assert(svcName == entry.svcName);
+
+ if (filter.empty()) {
+ return true;
+ } else {
+ return filter.match(entry.props);
+ }
+ }
+
+ void addMatch(std::shared_ptr<const SvcEntry> entry) {
+ //increase usage so that services cannot be removed while a service tracker is still active
+ entry->incrUsage();
+
+ {
+ std::lock_guard<std::mutex> lck{tracked.mutex};
+ tracked.entries.insert(entry);
+ }
+
+ //call callbacks
+ callSetCallbacks();
+ callAddRemoveCallbacks(entry, true);
+ callUpdateCallbacks();
+ }
+
+ void remMatch(const std::shared_ptr<const SvcEntry> &entry) {
+ {
+ std::lock_guard<std::mutex> lck{tracked.mutex};
+ tracked.entries.erase(entry);
+ }
+
+ //call callbacks
+ callSetCallbacks();
+ callAddRemoveCallbacks(entry, false);
+ callUpdateCallbacks();
+
+ //decrease usage so that services cannot be removed while a service tracker is still active
+ entry->decrUsage();
+ }
+
+ void callAddRemoveCallbacks(const std::shared_ptr<const SvcEntry> &updatedEntry, bool add) {
+ auto &update = add ? opts.add : opts.remove;
+ if (update != nullptr) {
+ void *svc = updatedEntry->service(*owner);
+ update(svc);
+ }
+ //TODO rest of add/remove
+ }
+
+ void callSetCallbacks() {
+ std::shared_ptr<const SvcEntry> currentHighest;
+ bool highestUpdated = false;
+ {
+ std::lock_guard<std::mutex> lck{tracked.mutex};
+ auto begin = tracked.entries.begin();
+ if (begin == tracked.entries.end()) {
+ currentHighest = nullptr;
+ } else {
+ currentHighest = *begin;
+ }
+ if (currentHighest != tracked.highest) {
+ tracked.highest = currentHighest;
+ highestUpdated = true;
+ }
+ }
+
+ //TODO race condition. highest can be updated because lock is released.
+
+ if (highestUpdated) {
+ void *svc = currentHighest == nullptr ? nullptr : currentHighest->service(*owner);
+ if (opts.set != nullptr) {
+ opts.set(svc);
+ }
+ //TODO rest of set
+ }
+ }
+
+ void callUpdateCallbacks() {
+ if (opts.update) {
+ std::vector<void *> rankedServices{};
+ {
+ std::lock_guard<std::mutex> lck{tracked.mutex};
+ rankedServices.reserve(tracked.entries.size());
+ for (auto &tracked : tracked.entries) {
+ rankedServices.push_back(tracked->service(*owner));
+ }
+ }
+ opts.update(std::move(rankedServices));
+ }
+ //TODO rest of the update calls
+ }
+
+ int count() const {
+ LOG(INFO) << "TODO use shared_ptr count instead";
+ std::lock_guard<std::mutex> lck{tracked.mutex};
+ return (int)tracked.entries.size();
+ }
+
+ void incrUsage() const {
+ LOG(INFO) << "TODO use shared_ptr count instead";
+ std::lock_guard<std::mutex> lck{mutex};
+ usage += 1;
+ }
+
+ void decrUsage() const {
+ LOG(INFO) << "TODO use shared_ptr count instead";
+ std::lock_guard<std::mutex> lck{mutex};
+ usage -= 1;
+ cond.notify_all();
+ }
+
+ void waitTillUnused() const {
+ std::unique_lock<std::mutex> lock{mutex};
+ cond.wait(lock, [this]{return usage == 0;});
+ }
+ private:
+ struct {
+ mutable std::mutex mutex; //protects matchedEntries & highestRanking
+ std::set<std::shared_ptr<const SvcEntry>, SvcEntryLess> entries{};
+ std::shared_ptr<const SvcEntry> highest{};
+ } tracked{};
+
+
+ //sync TODO refactor to atomics
+ mutable std::mutex mutex{};
+ mutable std::condition_variable cond{};
+ mutable int usage{1};
+ };
+}
+
+/**********************************************************************************************************************
+ Impl classes
+ **********************************************************************************************************************/
+
+class celix::ServiceRegistration::Impl {
+public:
+ const std::shared_ptr<const SvcEntry> entry;
+ const std::function<void()> unregisterCallback;
+ bool registered; //TODO make atomic?
+};
+
+class celix::ServiceTracker::Impl {
+public:
+ const std::shared_ptr<SvcTrackerEntry> entry;
+ const std::function<void()> untrackCallback;
+ bool active; //TODO make atomic?
+};
+
+class celix::ServiceRegistry::Impl {
+public:
+ const std::shared_ptr<const celix::IResourceBundle> emptyBundle = std::shared_ptr<const celix::IResourceBundle>{new EmptyBundle{}};
+ std::string regName;
+
+ struct {
+ mutable std::mutex mutex{};
+ long nextSvcId = 1L;
+
+ /* Services entries are always stored for it specific svc name.
+ * When using services is it always expected that the svc name is provided, and as such
+ * storing per svc name is more logical.
+ *
+ * Note that this also means that the classic 99% use cases used OSGi filter (objectClass=<svcName>)
+ * is not needed anymore -> simpler and faster.
+ *
+ * map key is svcName and the map value is a set of SvcEntry structs.
+ * note: The SvcEntry set is ordered (i.e. not a hashset with O(1) access) to ensure that service ranking and
+ * service id can be used for their position in the set (see SvcEntryLess).
+ */
+ std::unordered_map<std::string, std::set<std::shared_ptr<const SvcEntry>, SvcEntryLess>> registry{};
+
+ //Cache map for faster and easier access based on svcId.
+ std::unordered_map<long, std::shared_ptr<const SvcEntry>> cache{};
+ } services{};
+
+ struct {
+ mutable std::mutex mutex{};
+ long nextTrackerId = 1L;
+
+ /* Note same ordering as services registry, expect the ranking order requirement,
+ * so that it is easier and faster to update the trackers.
+ */
+ std::unordered_map<std::string, std::set<std::shared_ptr<SvcTrackerEntry>>> registry{};
+ std::unordered_map<long, std::shared_ptr<SvcTrackerEntry>> cache{};
+ } trackers{};
+
+ celix::ServiceRegistration registerService(std::string serviceName, std::shared_ptr<void> svc, std::shared_ptr<celix::IServiceFactory<void>> factory, celix::Properties props, std::shared_ptr<const celix::IResourceBundle> owner) {
+ props[celix::SERVICE_NAME] = std::move(serviceName);
+ std::string &svcName = props[celix::SERVICE_NAME];
+
+ std::lock_guard<std::mutex> lock{services.mutex};
+ long svcId = services.nextSvcId++;
+ props[celix::SERVICE_ID] = std::to_string(svcId);
+
+ //Add to registry
+ std::shared_ptr<const celix::IResourceBundle> bnd = owner ? owner : emptyBundle;
+
+ if (factory) {
+ VLOG(1) << "Registering service factory '" << svcName << "' from bundle id " << owner->id() << std::endl;
+ } else {
+ VLOG(1) << "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)});
+ assert(it.second); //should always lead to a new entry
+ const std::shared_ptr<const SvcEntry> &entry = *it.first;
+
+ //Add to svcId cache
+ services.cache[entry->svcId] = entry;
+
+ //update trackers
+ std::thread updateThread{[&]{updateTrackers(entry, true);}};
+ updateThread.join();
+ entry->decrUsage(); //note usage started at 1 during creation
+
+
+ //create unregister callback
+ std::function<void()> unreg = [this, svcId]() -> void {
+ this->unregisterService(svcId);
+ };
+ auto *impl = new celix::ServiceRegistration::Impl{
+ .entry = entry,
+ .unregisterCallback = std::move(unreg),
+ .registered = true
+ };
+ return celix::ServiceRegistration{impl};
+ }
+
+ void unregisterService(long svcId) {
+ if (svcId <= 0) {
+ return; //silent ignore
+ }
+
+ std::shared_ptr<const SvcEntry> match{nullptr};
+
+ {
+ std::lock_guard<std::mutex> lock{services.mutex};
+ const auto it = services.cache.find(svcId);
+ if (it != services.cache.end()) {
+ match = it->second;
+ services.cache.erase(it);
+ services.registry.at(match->svcName).erase(match);
+ }
+ }
+
+ if (match) {
+ std::thread updateThread{[&]{updateTrackers(match, false);}};
+ updateThread.join();
+ match->waitTillUnused();
+ } else {
+ LOG(WARNING) << "Cannot unregister service. Unknown service id: " << svcId << "." << std::endl;
+ }
+ }
+
+ void removeTracker(long trkId) {
+ if (trkId <= 0) {
+ return; //silent ignore
+ }
+
+ std::shared_ptr<SvcTrackerEntry> match{nullptr};
+ {
+ std::lock_guard<std::mutex> lock{trackers.mutex};
+ const auto it = trackers.cache.find(trkId);
+ if (it != trackers.cache.end()) {
+ match = it->second;
+ trackers.cache.erase(it);
+ trackers.registry.at(match->svcName).erase(match);
+ }
+ }
+
+ if (match) {
+ match->waitTillUnused();
+ std::thread clearThread{[&]{match->clear();}}; //ensure that all service are removed using the callbacks
+ clearThread.join();
+ } else {
+ LOG(WARNING) << "Cannot remove tracker. Unknown tracker id: " << trkId << "." << std::endl;
+ }
+ }
+
+ void updateTrackers(const std::shared_ptr<const SvcEntry> &entry, bool adding) {
+ std::vector<std::shared_ptr<SvcTrackerEntry>> matchedTrackers{};
+ {
+ std::lock_guard<std::mutex> lock{trackers.mutex};
+ for (auto &tracker : trackers.registry[entry->svcName]) {
+ if (tracker->match(*entry)) {
+ tracker->incrUsage();
+ matchedTrackers.push_back(tracker);
+ }
+ }
+ }
+
+ for (auto &match : matchedTrackers) {
+ if (adding) {
+ match->addMatch(entry);
+ } else {
+ match->remMatch(entry);
+ }
+ match->decrUsage();
+ }
+ }
+};
+
+
+
+/**********************************************************************************************************************
+ Service Registry
+ **********************************************************************************************************************/
+
+
+celix::ServiceRegistry::ServiceRegistry(std::string name) : pimpl{new ServiceRegistry::Impl{}} {
+ pimpl->regName = std::move(name);
+}
+celix::ServiceRegistry::ServiceRegistry(celix::ServiceRegistry &&rhs) = default;
+celix::ServiceRegistry& celix::ServiceRegistry::operator=(celix::ServiceRegistry &&rhs) = default;
+celix::ServiceRegistry::~ServiceRegistry() {
+ if (pimpl) {
+ //TODO
+ }
+}
+
+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));
+}
+
+celix::ServiceRegistration celix::ServiceRegistry::registerServiceFactory(std::string svcName, std::shared_ptr<celix::IServiceFactory<void>> factory, celix::Properties props, std::shared_ptr<const celix::IResourceBundle> owner) {
+ return pimpl->registerService(std::move(svcName), {}, std::move(factory), std::move(props), std::move(owner));
+}
+
+//TODO add useService(s) call to ServiceTracker object for fast service access
+
+//TODO move to Impl
+celix::ServiceTracker celix::ServiceRegistry::trackServices(std::string svcName, celix::ServiceTrackerOptions<void> options, std::shared_ptr<const celix::IResourceBundle> requester) {
+ //TODO create new tracker event and start new thread to update track trackers
+ long trkId = 0;
+ {
+ std::lock_guard<std::mutex> lck{pimpl->trackers.mutex};
+ trkId = pimpl->trackers.nextTrackerId++;
+ }
+
+ auto trkEntry = std::shared_ptr<SvcTrackerEntry>{new SvcTrackerEntry{trkId, requester, std::move(svcName), std::move(options)}};
+ if (trkEntry->valid()) {
+
+ //find initial services and add new tracker to registry.
+ //NOTE two locks to ensure no new services can be added/removed during initial tracker setup
+ std::vector<std::shared_ptr<const SvcEntry>> services{};
+ {
+ std::lock_guard<std::mutex> lck1{pimpl->services.mutex};
+ for (auto &svcEntry : pimpl->services.registry[trkEntry->svcName]) {
+ if (trkEntry->match(*svcEntry)) {
+ svcEntry->incrUsage();
+ services.push_back(svcEntry);
+ }
+ }
+
+ std::lock_guard<std::mutex> lck2{pimpl->trackers.mutex};
+ pimpl->trackers.registry[trkEntry->svcName].insert(trkEntry);
+ pimpl->trackers.cache[trkEntry->id] = trkEntry;
+ }
+ std::thread updateThread{[&]{
+ for (auto &svcEntry : services) {
+ trkEntry->addMatch(svcEntry);
+ svcEntry->decrUsage();
+ }
+ }};
+ updateThread.join();
+ trkEntry->decrUsage(); //note trkEntry usage started at 1
+
+ auto untrack = [this, trkId]() -> void {
+ this->pimpl->removeTracker(trkId);
+ };
+ auto *impl = new celix::ServiceTracker::Impl{
+ .entry = trkEntry,
+ .untrackCallback = std::move(untrack),
+ .active = true
+ };
+ return celix::ServiceTracker{impl};
+ } else {
+ trkEntry->decrUsage(); //note usage is 1 at creation
+ LOG(ERROR) << "Cannot create tracker. Invalid filter?" << std::endl;
+ return celix::ServiceTracker{nullptr};
+ }
+}
+
+//TODO move to Impl
+long celix::ServiceRegistry::nrOfServiceTrackers() const {
+ std::lock_guard<std::mutex> lck{pimpl->trackers.mutex};
+ return pimpl->trackers.cache.size();
+}
+
+//TODO unregister tracker with remove tracker event in a new thread
+//TODO move to Impl
+std::vector<long> celix::ServiceRegistry::findServices(const std::string &svcName, const std::string &rawFilter) const {
+ std::vector<long> result{};
+ celix::Filter filter = rawFilter;
+ if (!filter.valid()) {
+ LOG(WARNING) << "Invalid filter (" << rawFilter << ") provided. Cannot find services" << std::endl;
+ return result;
+ }
+
+ std::lock_guard<std::mutex> lock{pimpl->services.mutex};
+ const auto it = pimpl->services.registry.find(svcName);
+ if (it != pimpl->services.registry.end()) {
+ const auto &services = it->second;
+ for (const auto &visit : services) {
+ if (filter.empty() || filter.match(visit->props)) {
+ result.push_back(visit->svcId);
+ }
+ }
+ }
+ return result;
+}
+
+//TODO move to Impl
+long celix::ServiceRegistry::nrOfRegisteredServices() const {
+ std::lock_guard<std::mutex> lock{pimpl->services.mutex};
+ return pimpl->services.cache.size();
+}
+
+//TODO move to Impl
+int celix::ServiceRegistry::useServices(const std::string &svcName, std::function<void(void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> &use, const std::string &rawFilter, std::shared_ptr<const celix::IResourceBundle> requester) const {
+ celix::Filter filter = rawFilter;
+ if (!filter.valid()) {
+ LOG(WARNING) << "Invalid filter (" << rawFilter << ") provided. Cannot find services" << std::endl;
+ return 0;
+ }
+
+ std::vector<std::shared_ptr<const SvcEntry>> matches{};
+ {
+ std::lock_guard<std::mutex> lock{pimpl->services.mutex};
+ const auto it = pimpl->services.registry.find(svcName);
+ if (it != pimpl->services.registry.end()) {
+ const auto &services = it->second;
+ for (const std::shared_ptr<const SvcEntry> &entry : services) {
+ if (filter.empty() || filter.match(entry->props)) {
+ entry->incrUsage();
+ matches.push_back(entry);
+ }
+ }
+ }
+ }
+
+ for (const std::shared_ptr<const SvcEntry> &entry : matches) {
+ use(entry->service(*requester), entry->props, *entry->owner);
+ entry->decrUsage();
+ }
+
+ return (int)matches.size();
+}
+
+//TODO move to Impl
+bool celix::ServiceRegistry::useService(const std::string &svcName, std::function<void(void *svc, const celix::Properties &props, const celix::IResourceBundle &bnd)> &use, const std::string &rawFilter, std::shared_ptr<const celix::IResourceBundle> requester) const {
+ celix::Filter filter = rawFilter;
+ if (!filter.valid()) {
+ LOG(WARNING) << "Invalid filter (" << rawFilter << ") provided. Cannot find services" << std::endl;
+ return false;
+ }
+
+ std::shared_ptr<const SvcEntry> match = nullptr;
+ {
+ std::lock_guard<std::mutex> lock{pimpl->services.mutex};
+ const auto it = pimpl->services.registry.find(svcName);
+ if (it != pimpl->services.registry.end()) {
+ const auto &services = it->second;
+ for (const std::shared_ptr<const SvcEntry> &visit : services) {
+ if (filter.empty() || filter.match(visit->props)) {
+ visit->incrUsage();
+ match = visit;
+ break;
+ }
+ }
+ }
+ }
+
+ if (match != nullptr) {
+ use(match->service(*requester), match->props, *match->owner);
+ match->decrUsage();
+ }
+
+ return match != nullptr;
+}
+
+/**********************************************************************************************************************
+ Service Registration
+ **********************************************************************************************************************/
+
+celix::ServiceRegistration::ServiceRegistration() : pimpl{nullptr} {}
+
+celix::ServiceRegistration::ServiceRegistration(celix::ServiceRegistration::Impl *impl) : pimpl{impl} {}
+celix::ServiceRegistration::ServiceRegistration(celix::ServiceRegistration &&rhs) noexcept = default;
+celix::ServiceRegistration& celix::ServiceRegistration::operator=(celix::ServiceRegistration &&rhs) noexcept = default;
+celix::ServiceRegistration::~ServiceRegistration() { unregister(); }
+
+long celix::ServiceRegistration::serviceId() const { return pimpl ? pimpl->entry->svcId : -1L; }
+bool celix::ServiceRegistration::valid() const { return serviceId() >= 0; }
+bool celix::ServiceRegistration::factory() const { return pimpl && pimpl->entry->factory(); }
+bool celix::ServiceRegistration::registered() const {return pimpl && pimpl->registered; }
+
+void celix::ServiceRegistration::unregister() {
+ if (pimpl && pimpl->registered) {
+ pimpl->registered = false; //TODO make thread safe
+ pimpl->unregisterCallback();
+ }
+}
+
+const celix::Properties& celix::ServiceRegistration::properties() const {
+ static const celix::Properties empty{};
+ return pimpl ? pimpl->entry->props : empty;
+}
+
+const std::string& celix::ServiceRegistration::serviceName() const {
+ static const std::string empty{};
+ if (pimpl) {
+ return celix::getProperty(pimpl->entry->props, celix::SERVICE_NAME, empty);
+ }
+ return empty;
+}
+
+
+
+
+/**********************************************************************************************************************
+ Service Tracker
+ **********************************************************************************************************************/
+
+celix::ServiceTracker::ServiceTracker() : pimpl{nullptr} {}
+
+celix::ServiceTracker::ServiceTracker(celix::ServiceTracker::Impl *impl) : pimpl{impl} {}
+
+celix::ServiceTracker::~ServiceTracker() {
+ if (pimpl && pimpl->active) {
+ pimpl->untrackCallback();
+ }
+}
+
+
+void celix::ServiceTracker::stop() {
+ if (pimpl && pimpl->active) { //TODO make thread safe
+ pimpl->untrackCallback();
+ pimpl->active = false;
+ }
+}
+
+celix::ServiceTracker::ServiceTracker(celix::ServiceTracker &&rhs) noexcept = default;
+celix::ServiceTracker& celix::ServiceTracker::operator=(celix::ServiceTracker &&rhs) noexcept = default;
+
+int celix::ServiceTracker::trackCount() const { return pimpl ? pimpl->entry->count() : 0; }
+const std::string& celix::ServiceTracker::serviceName() const { return pimpl? pimpl->entry->svcName : emptyString; }
+const std::string& celix::ServiceTracker::filter() const { return pimpl ? pimpl->entry->filter.filterStr : emptyString; }
+bool celix::ServiceTracker::valid() const { return pimpl != nullptr; }
\ No newline at end of file