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 2021/05/05 20:45:05 UTC

[celix] branch feature/cxx_rsa_update updated: Refactors rsa configured discovery

This is an automated email from the ASF dual-hosted git repository.

pnoltes pushed a commit to branch feature/cxx_rsa_update
in repository https://gitbox.apache.org/repos/asf/celix.git


The following commit(s) were added to refs/heads/feature/cxx_rsa_update by this push:
     new a1e711f  Refactors rsa configured discovery
a1e711f is described below

commit a1e711f2012e7ff98bd37dfca89c7ba205e8cd16
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Wed May 5 22:44:39 2021 +0200

    Refactors rsa configured discovery
---
 bundles/cxx_remote_services/CMakeLists.txt         |   3 +-
 bundles/cxx_remote_services/admin/CMakeLists.txt   |  13 +-
 .../admin/gtest/src/RemoteServiceAdminTestSuite.cc |  26 ++-
 .../admin/include/RemoteServiceAdmin.h             |   8 +-
 .../admin/src/RemoteServiceAdmin.cc                |  26 +--
 .../common/include/ExportedServiceFactory.h        |   2 +-
 .../discovery_configured/CMakeLists.txt            |  44 ++---
 .../discovery_configured/gtest/CMakeLists.txt      |  30 ++++
 .../gtest/resources/endpoint_discovery.json        |  28 ++++
 .../gtest/src/RsaConfiguredDiscoveryTestSuite.cc   |  66 ++++++++
 .../include/ConfiguredDiscoveryManagerActivator.h  |  41 -----
 .../include/ConfiguredEndpoint.h                   |  52 ------
 .../include/ConfiguredEndpointProperties.h         |  99 -----------
 .../celix/rsa/IConfiguredDiscoveryManager.h}       |  23 +--
 .../src/ConfiguredDiscoveryManager.cc              |  99 +++++++----
 .../{include => src}/ConfiguredDiscoveryManager.h  |  49 ++----
 .../src/ConfiguredDiscoveryManagerActivator.cc     |  23 +--
 .../discovery_configured/src/ConfiguredEndpoint.cc |  89 ----------
 .../rsa_spi/include/celix/rsa/Endpoint.h           |  66 --------
 .../include/celix/rsa/EndpointDescription.h        | 186 +++++++++++++++++++++
 .../rsa_spi/include/celix/rsa/IEndpointAnnouncer.h |   6 +-
 .../include/celix/rsa/IExportServiceFactory.h      |   2 +-
 .../rsa_spi/include/celix/rsa/IExportedService.h   |   4 +-
 .../include/celix/rsa/IImportServiceFactory.h      |   4 +-
 .../rsa_spi/include/celix/rsa/RemoteConstants.h    | 133 +++++++++++++++
 .../include/celix/rsa/RemoteServicesException.h    |   2 +-
 libs/framework/gtest/CMakeLists.txt                |   1 +
 .../gtest/src/CxxBundleContextTestSuite.cc         |   1 -
 libs/framework/gtest/src/CxxUtilsTestSuite.cc      |  62 +++++++
 libs/framework/include/celix/Utils.h               |  20 +++
 30 files changed, 707 insertions(+), 501 deletions(-)

diff --git a/bundles/cxx_remote_services/CMakeLists.txt b/bundles/cxx_remote_services/CMakeLists.txt
index 2257092..eadbd41 100644
--- a/bundles/cxx_remote_services/CMakeLists.txt
+++ b/bundles/cxx_remote_services/CMakeLists.txt
@@ -17,10 +17,11 @@
 
 celix_subproject(REMOTE_SERVICE_ADMIN "Option to enable building the C++17 Remote Service Admin Service bundles" OFF)
 if (REMOTE_SERVICE_ADMIN)
+    message(WARNING "The C++ Remote Service Admin is still experimental; The API, SPI and implementation is not stable and will change")
     #add_subdirectory(common)
     add_subdirectory(rsa_spi)
     add_subdirectory(admin)
+    add_subdirectory(discovery_configured)
     #add_subdirectory(topology_manager)
-    #add_subdirectory(discovery_configured)
     #add_subdirectory(examples)
 endif()
\ No newline at end of file
diff --git a/bundles/cxx_remote_services/admin/CMakeLists.txt b/bundles/cxx_remote_services/admin/CMakeLists.txt
index 8cc9658..38bae70 100644
--- a/bundles/cxx_remote_services/admin/CMakeLists.txt
+++ b/bundles/cxx_remote_services/admin/CMakeLists.txt
@@ -17,7 +17,7 @@
 
 add_celix_bundle(RemoteServiceAdmin
         VERSION 0.9.0
-        SYMBOLIC_NAME "celix::RemoteServiceAdmin"
+        SYMBOLIC_NAME "apache::celix::RemoteServiceAdmin"
         NAME "Apache Celix Remote Service Admin"
         GROUP "Celix/RSA"
         FILENAME "Celix_RemoteServiceAdmin"
@@ -31,11 +31,10 @@ target_link_libraries(RemoteServiceAdmin PRIVATE
         Celix::log_service_api
 )
 
-if (ENABLE_TESTING)
-    add_subdirectory(gtest)
-endif()
-
-
 install_celix_bundle(RemoteServiceAdmin EXPORT celix COMPONENT rsa)
 #Setup target aliases to match external usage
-add_library(Celix::RemoteServiceAdmin ALIAS RemoteServiceAdmin)
\ No newline at end of file
+add_library(Celix::RemoteServiceAdmin ALIAS RemoteServiceAdmin)
+
+if (ENABLE_TESTING)
+    add_subdirectory(gtest)
+endif()
\ No newline at end of file
diff --git a/bundles/cxx_remote_services/admin/gtest/src/RemoteServiceAdminTestSuite.cc b/bundles/cxx_remote_services/admin/gtest/src/RemoteServiceAdminTestSuite.cc
index 22ff43e..c9d5c4e 100644
--- a/bundles/cxx_remote_services/admin/gtest/src/RemoteServiceAdminTestSuite.cc
+++ b/bundles/cxx_remote_services/admin/gtest/src/RemoteServiceAdminTestSuite.cc
@@ -23,7 +23,7 @@
 #include "celix/rsa/IExportServiceFactory.h"
 #include "celix/rsa/IExportedService.h"
 #include "celix/rsa/IImportServiceFactory.h"
-#include "celix/rsa/Constants.h"
+#include "celix/rsa/RemoteConstants.h"
 
 class RemoteServiceAdminTestSuite : public ::testing::Test {
 public:
@@ -52,11 +52,9 @@ TEST_F(RemoteServiceAdminTestSuite, startStopStartStopBundle) {
 class StubExportedService : public celix::rsa::IExportedService {
 public:
     ~StubExportedService() noexcept override = default;
-    std::shared_ptr<celix::rsa::Endpoint> getEndpoint() override {
-        return endpoint;
+    std::shared_ptr<celix::rsa::EndpointDescription> getEndpoint() override {
+        return nullptr; //can by nullptr for dummy
     }
-private:
-    std::shared_ptr<celix::rsa::Endpoint> endpoint = std::make_shared<celix::rsa::Endpoint>(celix::Properties{}); //can be empty for stub
 };
 
 
@@ -148,7 +146,7 @@ TEST_F(RemoteServiceAdminTestSuite, exportService) {
     EXPECT_EQ(0, count);
 
      auto reg2 = ctx->registerService<IDummyService>(std::make_shared<DummyServiceImpl>())
-             .addProperty(celix::rsa::REMOTE_SERVICE_EXPORTED_PROPERTY_NAME, true)
+             .addProperty(celix::rsa::SERVICE_EXPORTED_INTERFACES, "*")
              .build();
 
     //rsa called export service factory which created a IExportServiceRegistration, which register the marker interface IExportedService indicating an exported service
@@ -181,7 +179,7 @@ TEST_F(RemoteServiceAdminTestSuite, exportServiceDifferentOrder) {
     EXPECT_EQ(0, count);
 
     auto reg1 = ctx->registerService<IDummyService>(std::make_shared<DummyServiceImpl>())
-            .addProperty(celix::rsa::REMOTE_SERVICE_EXPORTED_PROPERTY_NAME, true)
+            .addProperty(celix::rsa::SERVICE_EXPORTED_INTERFACES, "*")
             .build();
 
     count = ctx->useService<celix::rsa::IExportedService>()
@@ -216,7 +214,7 @@ public:
     explicit StubImportedServiceEntry(std::shared_ptr<celix::BundleContext> _ctx) : ctx{std::move(_ctx)} {
         auto& cmp = ctx->getDependencyManager()->createComponent<StubImportedService>();
         cmp.createProvidedService<IDummyService>()
-                .addProperty(celix::rsa::REMOTE_SERVICE_IMPORTED_PROPERTY_NAME, true);
+                .addProperty(celix::rsa::SERVICE_EXPORTED_INTERFACES, "*");
         cmp.buildAsync();
         cmpUUID = cmp.getUUID();
     }
@@ -254,8 +252,8 @@ class StubImportServiceFactory : public celix::rsa::IImportServiceFactory {
 public:
     explicit StubImportServiceFactory(std::shared_ptr<celix::BundleContext> _ctx) : ctx{std::move(_ctx)} {}
 
-    virtual std::unique_ptr<celix::rsa::IImportServiceGuard> importService(const celix::rsa::Endpoint& endpoint) {
-           if (endpoint.getExportedInterfaces() == celix::typeName<IDummyService>()) {
+    virtual std::unique_ptr<celix::rsa::IImportServiceGuard> importService(const celix::rsa::EndpointDescription& endpoint) {
+           if (endpoint.getInterface() == celix::typeName<IDummyService>()) {
                std::lock_guard<std::mutex> lock{mutex};
                auto entry = std::make_shared<StubImportedServiceEntry>(ctx);
                entries.emplace_back(entry);
@@ -291,10 +289,10 @@ TEST_F(RemoteServiceAdminTestSuite, importService) {
             .build();
     EXPECT_EQ(0, count);
 
-    auto endpoint = std::make_shared<celix::rsa::Endpoint>(celix::Properties{
-        {celix::rsa::Endpoint::IDENTIFIER, "endpoint-id-1"},
-        {celix::rsa::Endpoint::EXPORTS, celix::typeName<IDummyService>()}});
-    auto reg2 = ctx->registerService<celix::rsa::Endpoint>(std::move(endpoint))
+    auto endpoint = std::make_shared<celix::rsa::EndpointDescription>(celix::Properties{
+        {celix::rsa::ENDPOINT_ID, "endpoint-id-1"},
+        {celix::SERVICE_NAME,celix::typeName<IDummyService>()}});
+    auto reg2 = ctx->registerService<celix::rsa::EndpointDescription>(std::move(endpoint))
             .build();
 
     //rsa called import service factory which created a IImportService, which register a IDummtService
diff --git a/bundles/cxx_remote_services/admin/include/RemoteServiceAdmin.h b/bundles/cxx_remote_services/admin/include/RemoteServiceAdmin.h
index f741af3..2d6b866 100644
--- a/bundles/cxx_remote_services/admin/include/RemoteServiceAdmin.h
+++ b/bundles/cxx_remote_services/admin/include/RemoteServiceAdmin.h
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-#include "celix/rsa/Endpoint.h"
+#include "celix/rsa/EndpointDescription.h"
 #include <celix_api.h>
 #include <mutex>
 #include "celix_log_service.h"
@@ -38,8 +38,8 @@ namespace celix::rsa {
     class RemoteServiceAdmin {
     public:
         // Imported endpoint add/remove functions
-        void addEndpoint(const std::shared_ptr<celix::rsa::Endpoint>& endpoint);
-        void removeEndpoint(const std::shared_ptr<celix::rsa::Endpoint>& endpoint);
+        void addEndpoint(const std::shared_ptr<celix::rsa::EndpointDescription>& endpoint);
+        void removeEndpoint(const std::shared_ptr<celix::rsa::EndpointDescription>& endpoint);
 
         // import service factory. used to create new imported services
         void addImportedServiceFactory(const std::shared_ptr<celix::rsa::IImportServiceFactory>& factory, const std::shared_ptr<const celix::Properties>& properties);
@@ -75,7 +75,7 @@ namespace celix::rsa {
         std::unordered_map<std::string, std::unique_ptr<celix::rsa::IImportServiceGuard>> _importedServices{}; //key = endpoint id
         std::unordered_map<long, std::unique_ptr<celix::rsa::IExportServiceGuard>> _exportedServices{}; //key = service id
 #endif
-        std::vector<std::shared_ptr<celix::rsa::Endpoint>> _toBeImportedServices{};
+        std::vector<std::shared_ptr<celix::rsa::EndpointDescription>> _toBeImportedServices{};
         std::vector<std::shared_ptr<const celix::Properties>> _toBeExportedServices{};
     };
 }
\ No newline at end of file
diff --git a/bundles/cxx_remote_services/admin/src/RemoteServiceAdmin.cc b/bundles/cxx_remote_services/admin/src/RemoteServiceAdmin.cc
index be35f71..4bdcc49 100644
--- a/bundles/cxx_remote_services/admin/src/RemoteServiceAdmin.cc
+++ b/bundles/cxx_remote_services/admin/src/RemoteServiceAdmin.cc
@@ -18,7 +18,9 @@
  */
 
 #include "RemoteServiceAdmin.h"
-#include "celix/rsa/Constants.h"
+#include "celix/rsa/RemoteConstants.h"
+#include "celix/rsa/IImportServiceFactory.h"
+#include "celix/rsa/IExportServiceFactory.h"
 
 #define L_DEBUG(...) \
     if (_logService) {                                                  \
@@ -37,16 +39,16 @@
         _logService->error(_logService->handle, __VA_ARGS__);           \
     }
 
-void celix::rsa::RemoteServiceAdmin::addEndpoint(const std::shared_ptr<celix::rsa::Endpoint>& endpoint) {
+void celix::rsa::RemoteServiceAdmin::addEndpoint(const std::shared_ptr<celix::rsa::EndpointDescription>& endpoint) {
     assert(endpoint);
 
-    auto interface = endpoint->getExportedInterfaces();
+    auto interface = endpoint->getInterface();
     if (interface.empty()) {
         L_WARN("Adding endpoint but missing exported interfaces");
         return;
     }
 
-    auto endpointId = endpoint->getEndpointId();
+    auto endpointId = endpoint->getId();
     if (endpointId.empty()) {
         L_WARN("Adding endpoint but missing service id");
         return;
@@ -57,10 +59,10 @@ void celix::rsa::RemoteServiceAdmin::addEndpoint(const std::shared_ptr<celix::rs
     createImportServices();
 }
 
-void celix::rsa::RemoteServiceAdmin::removeEndpoint(const std::shared_ptr<celix::rsa::Endpoint>& endpoint) {
+void celix::rsa::RemoteServiceAdmin::removeEndpoint(const std::shared_ptr<celix::rsa::EndpointDescription>& endpoint) {
     assert(endpoint);
 
-    auto id = endpoint->getEndpointId();
+    auto id = endpoint->getId();
     if (id.empty()) {
         L_WARN("Cannot remove endpoint without a valid id");
         return;
@@ -71,7 +73,7 @@ void celix::rsa::RemoteServiceAdmin::removeEndpoint(const std::shared_ptr<celix:
         std::lock_guard l(_m);
 
         _toBeImportedServices.erase(std::remove_if(_toBeImportedServices.begin(), _toBeImportedServices.end(), [&id](auto const &endpoint){
-            return id == endpoint->getEndpointId();
+            return id == endpoint->getId();
         }), _toBeImportedServices.end());
 
         auto it = _importedServices.find(id);
@@ -176,7 +178,7 @@ void celix::rsa::RemoteServiceAdmin::removeService(const std::shared_ptr<void>&
 
     //remove to be exported endpoint (if present)
     for (auto it = _toBeExportedServices.begin(); it != _toBeExportedServices.end(); ++it) {
-        if ((*it)->getAsBool(celix::SERVICE_ID, -1) == svcId) {
+        if ((*it)->getAsLong(celix::SERVICE_ID, -1) == svcId) {
             _toBeExportedServices.erase(it);
             break;
         }
@@ -186,14 +188,14 @@ void celix::rsa::RemoteServiceAdmin::removeService(const std::shared_ptr<void>&
 void celix::rsa::RemoteServiceAdmin::createImportServices() {
     auto it = _toBeImportedServices.begin();
     while (it != _toBeImportedServices.end()) {
-        auto interface = (*it)->getExportedInterfaces();
+        auto interface = (*it)->getInterface();
         auto existingFactory = _importServiceFactories.find(interface);
         if (existingFactory == end(_importServiceFactories)) {
             L_DEBUG("Adding endpoint to be imported but no factory available yet, delaying import");
             ++it;
             continue;
         }
-        auto endpointId = (*it)->getEndpointId();
+        auto endpointId = (*it)->getId();
         L_DEBUG("Adding endpoint, created service");
         _importedServices.emplace(endpointId, existingFactory->second->importService(**it));
         it = _toBeImportedServices.erase(it);
@@ -231,7 +233,7 @@ public:
         auto admin = std::make_shared<celix::rsa::RemoteServiceAdmin>();
 
         auto& cmp = ctx->getDependencyManager()->createComponent(admin);
-        cmp.createServiceDependency<celix::rsa::Endpoint>()
+        cmp.createServiceDependency<celix::rsa::EndpointDescription>()
                 .setRequired(false)
                 .setStrategy(celix::dm::DependencyUpdateStrategy::locking)
                 .setCallbacks(&celix::rsa::RemoteServiceAdmin::addEndpoint, &celix::rsa::RemoteServiceAdmin::removeEndpoint);
@@ -252,7 +254,7 @@ public:
 
         //note adding void service dependencies is not supported for the dependency manager, using a service tracker instead.
         _remoteServiceTracker = ctx->trackAnyServices()
-                .setFilter(std::string{"("}.append(celix::rsa::REMOTE_SERVICE_EXPORTED_PROPERTY_NAME).append("=*)"))
+                .setFilter(std::string{"("}.append(celix::rsa::SERVICE_EXPORTED_INTERFACES).append("=*)"))
                 .addAddWithPropertiesCallback([admin](const std::shared_ptr<void>& svc, const std::shared_ptr<const celix::Properties>& properties) {
                     admin->addService(svc, properties);
                 })
diff --git a/bundles/cxx_remote_services/common/include/ExportedServiceFactory.h b/bundles/cxx_remote_services/common/include/ExportedServiceFactory.h
index 6dcc0c9..1db4bae 100644
--- a/bundles/cxx_remote_services/common/include/ExportedServiceFactory.h
+++ b/bundles/cxx_remote_services/common/include/ExportedServiceFactory.h
@@ -21,7 +21,7 @@
 #include <celix/dm/DependencyManager.h>
 #include <pubsub_endpoint.h>
 #include <IExportedService.h>
-#include "celix/rsa/Endpoint.h"
+#include "celix/rsa/EndpointDescription.h"
 
 namespace celix::async_rsa {
     /// Service factory interface.
diff --git a/bundles/cxx_remote_services/discovery_configured/CMakeLists.txt b/bundles/cxx_remote_services/discovery_configured/CMakeLists.txt
index 8a38a11..5301efb 100644
--- a/bundles/cxx_remote_services/discovery_configured/CMakeLists.txt
+++ b/bundles/cxx_remote_services/discovery_configured/CMakeLists.txt
@@ -17,27 +17,31 @@
 
 find_package(RapidJSON REQUIRED)
 
-add_celix_bundle(async_rsa_discovery_configured
-        VERSION 0.9.0
-        SYMBOLIC_NAME "apache_celix_async_remote_service_admin_discovery_configured"
-        NAME "Apache Celix Async Configured Discovery"
-        GROUP "Celix/AsyncRSA"
-        SOURCES
-        src/ConfiguredEndpoint.cc
+#TODO alias and install lib
+add_library(RsaConfiguredDiscovery_api INTERFACE)
+target_include_directories(RsaConfiguredDiscovery_api INTERFACE
+        include
+)
+
+add_celix_bundle(RsaConfiguredDiscovery
+    VERSION 0.9.0
+    SYMBOLIC_NAME "apache::celix::RsaConfiguredDiscovery"
+    NAME "Apache Celix Async Configured Discovery"
+    GROUP "Celix/RSA"
+    SOURCES
         src/ConfiguredDiscoveryManager.cc
         src/ConfiguredDiscoveryManagerActivator.cc
-        )
-target_include_directories(async_rsa_discovery_configured PUBLIC
-        $<BUILD_INTERFACE:${RapidJSON_INCLUDE_DIR}>
-        $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>
-        $<INSTALL_INTERFACE:include/celix/async_rsa_discovery_configured>)
-target_link_libraries(async_rsa_discovery_configured PUBLIC
-        Celix::async_rsa_common
+)
+target_link_libraries(RsaConfiguredDiscovery PRIVATE
+        RsaConfiguredDiscovery_api
         Celix::framework
         Celix::rsa_spi
-        )
-install_celix_bundle(async_rsa_discovery_configured EXPORT celix COMPONENT async_rsa_discovery_configured)
-target_compile_options(async_rsa_discovery_configured INTERFACE -std=c++17)
-add_library(Celix::async_rsa_discovery_configured ALIAS async_rsa_discovery_configured)
-install(TARGETS async_rsa_discovery_configured EXPORT celix DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT async_rsa_discovery_configured)
-install(DIRECTORY include/ DESTINATION include/celix/async_rsa_discovery_configured COMPONENT async_rsa_discovery_configured)
\ No newline at end of file
+)
+target_include_directories(RsaConfiguredDiscovery PRIVATE src)
+install_celix_bundle(RsaConfiguredDiscovery EXPORT celix COMPONENT rsa)
+target_compile_options(RsaConfiguredDiscovery INTERFACE -std=c++17)
+add_library(Celix::RsaConfiguredDiscovery ALIAS RsaConfiguredDiscovery)
+
+if (ENABLE_TESTING)
+    add_subdirectory(gtest)
+endif()
diff --git a/bundles/cxx_remote_services/discovery_configured/gtest/CMakeLists.txt b/bundles/cxx_remote_services/discovery_configured/gtest/CMakeLists.txt
new file mode 100644
index 0000000..710440b
--- /dev/null
+++ b/bundles/cxx_remote_services/discovery_configured/gtest/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+add_executable(test_cxx_rsa_configured_discovery
+        src/RsaConfiguredDiscoveryTestSuite.cc
+)
+
+target_link_libraries(test_cxx_rsa_configured_discovery PRIVATE Celix::framework GTest::gtest GTest::gtest_main Celix::rsa_spi RsaConfiguredDiscovery_api)
+
+add_dependencies(test_cxx_rsa_configured_discovery RsaConfiguredDiscovery_bundle)
+celix_get_bundle_file(RsaConfiguredDiscovery RSA_CONFIGURED_DISCOVERY_BUNDLE_LOCATION)
+target_compile_definitions(test_cxx_rsa_configured_discovery PRIVATE RSA_CONFIGURED_DISCOVERY_BUNDLE_LOCATION="${RSA_CONFIGURED_DISCOVERY_BUNDLE_LOCATION}")
+target_compile_definitions(test_cxx_rsa_configured_discovery PRIVATE RSA_CONFIGURED_DISCOVERY_DISCOVERY_FILE="${CMAKE_CURRENT_SOURCE_DIR}/resources/endpoint_discovery.json")
+
+add_test(NAME test_cxx_rsa_configured_discovery COMMAND test_cxx_rsa_configured_discovery)
+setup_target_for_coverage(test_cxx_rsa_configured_discovery SCAN_DIR ..)
\ No newline at end of file
diff --git a/bundles/cxx_remote_services/discovery_configured/gtest/resources/endpoint_discovery.json b/bundles/cxx_remote_services/discovery_configured/gtest/resources/endpoint_discovery.json
new file mode 100644
index 0000000..a491180
--- /dev/null
+++ b/bundles/cxx_remote_services/discovery_configured/gtest/resources/endpoint_discovery.json
@@ -0,0 +1,28 @@
+{
+  "endpoints": [
+    {
+      "endpoint.id": "id-01",
+      "service.imported": true,
+      "service.imported.configs": [
+        "pubsub"
+      ],
+      "service.exported.interfaces": "IHardcodedService",
+      "endpoint.objectClass": "TestComponentName",
+      "endpoint.scope": "default",
+      "endpoint.topic": "default",
+      "endpoint.anykey" : "anyval"
+    },
+    {
+      "endpoint.id": "id-02",
+      "service.imported": true,
+      "service.imported.configs": [
+        "pubsub"
+      ],
+      "service.exported.interfaces": "*",
+      "endpoint.objectClass": "TestComponentName",
+      "endpoint.scope": "default",
+      "endpoint.topic": "default",
+      "endpoint.anykey" : "anyval"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/bundles/cxx_remote_services/discovery_configured/gtest/src/RsaConfiguredDiscoveryTestSuite.cc b/bundles/cxx_remote_services/discovery_configured/gtest/src/RsaConfiguredDiscoveryTestSuite.cc
new file mode 100644
index 0000000..cd67aef
--- /dev/null
+++ b/bundles/cxx_remote_services/discovery_configured/gtest/src/RsaConfiguredDiscoveryTestSuite.cc
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "celix/FrameworkFactory.h"
+#include "celix/rsa/EndpointDescription.h"
+#include "celix/rsa/IConfiguredDiscoveryManager.h"
+
+class RsaConfiguredDiscoveryTestSuite : public ::testing::Test {
+public:
+    RsaConfiguredDiscoveryTestSuite() {
+        celix::Properties config{};
+        config[celix::rsa::CONFIGURED_DISCOVERY_DISCOVERY_FILES] = std::string{RSA_CONFIGURED_DISCOVERY_DISCOVERY_FILE}.append("  ,  garbage_path, garbage_path2  ");
+        fw = celix::createFramework(config);
+        ctx = fw->getFrameworkBundleContext();
+    }
+
+    std::shared_ptr<celix::Framework> fw{};
+    std::shared_ptr<celix::BundleContext> ctx{};
+};
+
+TEST_F(RsaConfiguredDiscoveryTestSuite, startStopStartStopBundle) {
+    auto bndId = ctx->installBundle(RSA_CONFIGURED_DISCOVERY_BUNDLE_LOCATION);
+    EXPECT_GE(bndId, 0);
+    ctx->stopBundle(bndId);
+    ctx->startBundle(bndId);
+}
+
+TEST_F(RsaConfiguredDiscoveryTestSuite, discoverConfiguredEndpoints) {
+    auto bndId = ctx->installBundle(RSA_CONFIGURED_DISCOVERY_BUNDLE_LOCATION);
+    EXPECT_GE(bndId, 0);
+
+    //When I start the RSA Configured Discovery bundle with a endpoint discovery file with 2 endpoints
+    //there will be 2 Endpoint services in the framework.
+    auto count = ctx->useServices<celix::rsa::EndpointDescription>()
+            .addUseCallback([](auto& endpoint) {
+                EXPECT_NE(endpoint.getId(), "");
+                //TODO EXPECT_NE(endpoint.getConfigurationTypes(), "");
+                EXPECT_NE(endpoint.getInterface(), "");
+                EXPECT_NE(endpoint.getFrameworkUUID(), "");
+                EXPECT_NE(endpoint.getProperties().get("endpoint.scope"), ""); //note async specific
+                EXPECT_NE(endpoint.getProperties().get("endpoint.topic"), ""); //note async specific
+                EXPECT_NE(endpoint.getProperties().get("endpoint.anykey"), ""); //note test specific
+            })
+            .build();
+    EXPECT_EQ(count, 2);
+}
+
+//TODO test add/remove of adding configured discovery file
\ No newline at end of file
diff --git a/bundles/cxx_remote_services/discovery_configured/include/ConfiguredDiscoveryManagerActivator.h b/bundles/cxx_remote_services/discovery_configured/include/ConfiguredDiscoveryManagerActivator.h
deleted file mode 100644
index a5307f8..0000000
--- a/bundles/cxx_remote_services/discovery_configured/include/ConfiguredDiscoveryManagerActivator.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-#pragma once
-
-#include <ConfiguredDiscoveryManager.h>
-#include <celix_api.h>
-
-namespace {
-
-class ConfiguredDiscoveryManagerActivator {
-public:
-    explicit ConfiguredDiscoveryManagerActivator(const std::shared_ptr<celix::dm::DependencyManager>& dependencyManager);
-
-    ~ConfiguredDiscoveryManagerActivator();
-
-    ConfiguredDiscoveryManagerActivator(const ConfiguredDiscoveryManagerActivator&) = default;
-    ConfiguredDiscoveryManagerActivator(ConfiguredDiscoveryManagerActivator&&) = default;
-
-private:
-
-    celix::dm::Component<celix::rsa::ConfiguredDiscoveryManager>& _component;
-    std::shared_ptr<celix::dm::DependencyManager> _mng;
-};
-
-} // end anonymous namespace.
diff --git a/bundles/cxx_remote_services/discovery_configured/include/ConfiguredEndpoint.h b/bundles/cxx_remote_services/discovery_configured/include/ConfiguredEndpoint.h
deleted file mode 100644
index a107397..0000000
--- a/bundles/cxx_remote_services/discovery_configured/include/ConfiguredEndpoint.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.
- */
-#pragma once
-
-#include "celix/rsa/Endpoint.h"
-#include <ConfiguredEndpointProperties.h>
-
-#include <optional>
-#include <string>
-
-#include <rapidjson/document.h>
-
-namespace celix::rsa {
-
-/**
- * This subclass of Endpoint contains logic to parse (rapid) JSON values into endpoint celix properties.
- */
-class ConfiguredEndpoint : public Endpoint {
-public:
-
-    ConfiguredEndpoint() = delete;
-
-    explicit ConfiguredEndpoint(const rapidjson::Value& endpointJson);
-
-    const ConfiguredEndpointProperties& getConfiguredProperties() const;
-
-    std::string ToString() const {
-        return "[ConfiguredEndpoint (" + _configuredProperties->ToString() + ")]";
-    }
-
-private:
-
-    std::optional<ConfiguredEndpointProperties> _configuredProperties;
-};
-
-} // end namespace celix::rsa.
diff --git a/bundles/cxx_remote_services/discovery_configured/include/ConfiguredEndpointProperties.h b/bundles/cxx_remote_services/discovery_configured/include/ConfiguredEndpointProperties.h
deleted file mode 100644
index 63cce90..0000000
--- a/bundles/cxx_remote_services/discovery_configured/include/ConfiguredEndpointProperties.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.
- */
-#pragma once
-
-#include <string>
-#include <vector>
-
-namespace celix::rsa {
-
-/**
- * Data object for the ConfiguredEndpoint class, containing all data parsed from input JSON objects.
- */
-class ConfiguredEndpointProperties {
-public:
-
-    ConfiguredEndpointProperties(
-            std::string id, bool imported, std::vector <std::string> importConfigs,
-            std::string exports, std::vector <std::string> objectClass,
-            std::string scope, std::string topic) :
-            _id{std::move(id)},
-            _imported{imported},
-            _importConfigs{std::move(importConfigs)},
-            _exports{std::move(exports)},
-            _objectClass{std::move(objectClass)},
-            _scope{std::move(scope)},
-            _topic{std::move(topic)} {
-    }
-
-    std::string getId() const {
-
-        return _id;
-    }
-
-    bool isImported() const {
-
-        return _imported;
-    }
-
-    const std::vector <std::string>& getImportConfigs() const {
-
-        return _importConfigs;
-    }
-
-    const std::string& getExports() const {
-
-        return _exports;
-    }
-
-    const std::vector <std::string>& getObjectClass() const {
-
-        return _objectClass;
-    }
-
-    const std::string& getScope() const {
-
-        return _scope;
-    }
-
-    const std::string& getTopic() const {
-
-        return _topic;
-    }
-
-    std::string ToString() const {
-
-        return "id: " + _id + ", imported: " + std::to_string(_imported) +
-               ", imports (size): " + std::to_string(_importConfigs.size()) + ", exports: " + _exports +
-               ", objclass (size) " + std::to_string(_objectClass.size()) + ", scope: "
-               + _scope + ", topic: " + _topic;
-    }
-
-private:
-
-    std::string _id;
-    bool _imported;
-    std::vector <std::string> _importConfigs;
-    std::string _exports;
-    std::vector <std::string> _objectClass;
-    std::string _scope;
-    std::string _topic;
-};
-
-} // end namespace celix::rsa.
diff --git a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/Constants.h b/bundles/cxx_remote_services/discovery_configured/include/celix/rsa/IConfiguredDiscoveryManager.h
similarity index 59%
rename from bundles/cxx_remote_services/rsa_spi/include/celix/rsa/Constants.h
rename to bundles/cxx_remote_services/discovery_configured/include/celix/rsa/IConfiguredDiscoveryManager.h
index 5ae1c9f..4e7967a 100644
--- a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/Constants.h
+++ b/bundles/cxx_remote_services/discovery_configured/include/celix/rsa/IConfiguredDiscoveryManager.h
@@ -16,22 +16,25 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 #pragma once
 
-
 namespace celix::rsa {
-
     /**
-     * @brief Service property (named "service.exported") to mark a service as wanting to be exported (remote).
-     * Present means -> export service as a remote service.
+     * @brief Config property for the ',' seperated paths for  configured endpoints files.
      */
-    constexpr const char * const REMOTE_SERVICE_EXPORTED_PROPERTY_NAME = "service.exported";
+    constexpr const char *const CONFIGURED_DISCOVERY_DISCOVERY_FILES = "CELIX_RSA_CONFIGURED_DISCOVERY_DISCOVERY_FILES";
 
     /**
-     * @brief Service property (named "remote") to mark a service as a imported service.
-     * Present means -> service is a imported service (proxy of a remote service).
+     * @brief The IConfiguredDiscoveryManager interface.
+     *
+     * TODO document the expect json format.
      */
-    constexpr const char * const REMOTE_SERVICE_IMPORTED_PROPERTY_NAME = "service.imported";
+    class IConfiguredDiscoveryManager {
+    public:
+        virtual ~IConfiguredDiscoveryManager() noexcept = default;
+
+        virtual void addConfiguredDiscoveryFile(const std::string& path) = 0;
 
-}
\ No newline at end of file
+        virtual void removeConfiguredDiscoveryFile(const std::string& path) = 0;
+    };
+}
diff --git a/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManager.cc b/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManager.cc
index fc3207c..53d4d9e 100644
--- a/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManager.cc
+++ b/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManager.cc
@@ -22,16 +22,13 @@
 #include <fstream>
 #include <filesystem>
 
-#include <ConfiguredEndpoint.h>
 #include <celix_bundle_context.h>
 
 #include <rapidjson/writer.h>
 
-namespace celix::rsa {
+static constexpr const char* ENDPOINT_ARRAY = "endpoints";
 
-constexpr const char* ENDPOINT_ARRAY = "endpoints";
-
-std::optional<std::string> readFile(const std::string& path) {
+static std::optional<std::string> readFile(const std::string& path) {
 
     std::string contents;
     std::ifstream file(path);
@@ -41,51 +38,97 @@ std::optional<std::string> readFile(const std::string& path) {
     file.seekg(0, std::ios::end);
     contents.resize(file.tellg());
     file.seekg(0, std::ios::beg);
-    file.read(&contents[0], contents.size());
+    file.read(&contents[0], (std::streamsize)contents.size());
     file.close();
     return contents;
 }
 
-rapidjson::Document parseJSONFile(std::string& contents)  {
+static rapidjson::Document parseJSONFile(std::string& contents)  {
 
     rapidjson::Document resultDocument{};
     resultDocument.ParseInsitu(contents.data());
     return resultDocument;
 }
 
-ConfiguredDiscoveryManager::ConfiguredDiscoveryManager(std::shared_ptr<DependencyManager> dependencyManager) :
-        _dependencyManager{std::move(dependencyManager)},
-        _configurationFilePath{} {
-
-    _configurationFilePath = celix_bundleContext_getProperty(
-            _dependencyManager->bundleContext(),
-            CELIX_ASYNC_RSA_CONFIGURED_DISCOVERY_FILE,
-            "");
+celix::rsa::ConfiguredDiscoveryManager::ConfiguredDiscoveryManager(std::shared_ptr<celix::BundleContext> _ctx) :
+        ctx{std::move(_ctx)},
+        configuredDiscoveryFiles{ctx->getConfigProperty(celix::rsa::CONFIGURED_DISCOVERY_DISCOVERY_FILES, "")} {
+    readConfiguredDiscoveryFiles();
+}
 
-    discoverEndpoints();
+void celix::rsa::ConfiguredDiscoveryManager::readConfiguredDiscoveryFiles() {
+    //TODO split configuredDiscoveryFiles
+    if (!configuredDiscoveryFiles.empty()) {
+        for (auto path : celix::split(configuredDiscoveryFiles)) {
+            addConfiguredDiscoveryFile(path);
+        }
+    }
 }
 
-void ConfiguredDiscoveryManager::discoverEndpoints() {
+//static std::vector<std::string> parseJSONStringArray(const rapidjson::Value& jsonArray) {
+//    std::vector<std::string> resultVec{};
+//    if (jsonArray.IsArray()) {
+//        resultVec.reserve(jsonArray.Size());
+//        for (auto& element : jsonArray.GetArray()) {
+//            if (element.IsString()) {
+//                resultVec.emplace_back(element.GetString());
+//            }
+//        }
+//    }
+//    return resultVec;
+//}
+
+celix::Properties celix::rsa::ConfiguredDiscoveryManager::convertToEndpointProperties(const rapidjson::Value &endpointJSON) {
+    celix::Properties result{};
+    result.set(celix::rsa::ENDPOINT_FRAMEWORK_UUID, ctx->getFramework()->getUUID());
+    for (auto it = endpointJSON.MemberBegin(); it != endpointJSON.MemberEnd(); ++it) {
+        if (it->value.GetType() == rapidjson::Type::kStringType) {
+            if (celix_utils_stringEquals(it->name.GetString(), "endpoint.objectClass")) { //TODO improve
+                result.set(celix::SERVICE_NAME, it->value.GetString());
+            } else {
+                result.set(it->name.GetString(), it->value.GetString());
+            }
+        } else if (it->value.GetType() == rapidjson::Type::kTrueType || it->value.GetType() == rapidjson::Type::kFalseType) {
+            result.set(it->name.GetString(), it->value.GetBool());
+        } else {
+            std::cout << "TODO member " << it->name.GetString() << std::endl;
+        }
+    }
+    return result;
+}
 
-    auto contents = readFile(_configurationFilePath);
+void celix::rsa::ConfiguredDiscoveryManager::addConfiguredDiscoveryFile(const std::string& path) {
+    std::vector<std::shared_ptr<celix::ServiceRegistration>> newEndpoints{};
+    auto contents = readFile(path);
     if (contents) {
         auto parsedJson = parseJSONFile(contents.value());
         if (parsedJson.IsObject()) {
             if (parsedJson.HasMember(ENDPOINT_ARRAY) && parsedJson[ENDPOINT_ARRAY].IsArray()) {
 
-                for (auto& endpoint : parsedJson[ENDPOINT_ARRAY].GetArray()) {
-                    const auto& configuredEndpointPtr = std::make_shared<ConfiguredEndpoint>(endpoint.GetObject());
-                    const auto endpointProperties = configuredEndpointPtr->getProperties();
-
-                    std::cout << configuredEndpointPtr->ToString() << std::endl;
-
-                    _dependencyManager->createComponent<Endpoint>(configuredEndpointPtr)
-                                       .addInterface<Endpoint>("1.0.0", endpointProperties)
-                                       .build();
+                for (auto& jsonEndpoint : parsedJson[ENDPOINT_ARRAY].GetArray()) {
+                    try {
+                        auto endpointProperties = convertToEndpointProperties(jsonEndpoint);
+                        auto endpointDescription = std::make_shared<EndpointDescription>(std::move(endpointProperties));
+
+                        //TODO log service
+                        std::cout << endpointDescription->toString() << std::endl;
+
+                        auto reg = ctx->registerService<celix::rsa::EndpointDescription>(std::move(endpointDescription))
+                                .build();
+                        newEndpoints.emplace_back(std::move(reg));
+                    } catch (celix::rsa::RemoteServicesException& exp) {
+                        //TODO log service
+                        std::cerr << exp.what() << std::endl;
+                    }
                 }
             }
         }
+        std::lock_guard<std::mutex> lock{mutex};
+        endpointRegistrations.emplace(path, std::move(newEndpoints));
     }
 }
 
-} // end namespace celix::rsa.
+void celix::rsa::ConfiguredDiscoveryManager::removeConfiguredDiscoveryFile(const std::string& path) {
+    std::lock_guard<std::mutex> lock{mutex};
+    endpointRegistrations.erase(path);
+}
diff --git a/bundles/cxx_remote_services/discovery_configured/include/ConfiguredDiscoveryManager.h b/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManager.h
similarity index 53%
rename from bundles/cxx_remote_services/discovery_configured/include/ConfiguredDiscoveryManager.h
rename to bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManager.h
index f4646c6..7797471 100644
--- a/bundles/cxx_remote_services/discovery_configured/include/ConfiguredDiscoveryManager.h
+++ b/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManager.h
@@ -24,16 +24,14 @@
 #include <vector>
 #include <string>
 
-#include "celix/rsa/Endpoint.h"
-#include <celix_api.h>
-
-#include <ConfiguredEndpoint.h>
+#include "celix/rsa/EndpointDescription.h"
+#include "celix/rsa/IConfiguredDiscoveryManager.h"
+#include "celix_api.h"
 
 #include <rapidjson/document.h>
 #include <rapidjson/stringbuffer.h>
 
-/** Path for configured endpoint file. */
-#define CELIX_ASYNC_RSA_CONFIGURED_DISCOVERY_FILE "CELIX_ASYNC_RSA_CONFIGURED_DISCOVERY_FILE"
+/** Path for configured endpoints file. */
 
 namespace celix::rsa {
 
@@ -42,45 +40,30 @@ namespace celix::rsa {
  * a local configuration JSON file.
  * This configured discovery manager announces local exported endpoints and imported endpoints from the JSON file.
  */
-class ConfiguredDiscoveryManager final : public IEndpointAnnouncer {
+class ConfiguredDiscoveryManager final : public IConfiguredDiscoveryManager, public IEndpointAnnouncer {
 public:
-
-    /**
-     * Deleted default constructor, dependencyManager parameter is required.
-     */
-    ConfiguredDiscoveryManager() = delete;
-
     /**
      *  Constructor for the ConfiguredDiscoveryManager.
      * @param dependencyManager shared_ptr to the context/container dependency manager.
      */
-    explicit ConfiguredDiscoveryManager(std::shared_ptr<DependencyManager> dependencyManager);
+    explicit ConfiguredDiscoveryManager(std::shared_ptr<celix::BundleContext> ctx);
 
-    /**
-     * Deleted copy-constructor, since rapidjson members have no copy-constructor.
-     */
-    ConfiguredDiscoveryManager(const ConfiguredDiscoveryManager&) = delete;
-
-    /**
-     * Defaulted move-constructor.
-     */
-    ConfiguredDiscoveryManager(ConfiguredDiscoveryManager&&) = default;
-
-    /**
-     * Deleted assignment-operator, since rapidjson members have no copy-constructor.
-     */
-    ConfiguredDiscoveryManager& operator=(const ConfiguredDiscoveryManager&) = delete;
+    void announceEndpoint(std::unique_ptr<EndpointDescription> /*endpoint*/) override {/*nop*/}
 
-    void announceEndpoint(std::unique_ptr<Endpoint> /*endpoint*/) override {/*nop*/}
+    void revokeEndpoint(std::unique_ptr<EndpointDescription> /*endpoint*/) override {/*nop*/}
 
-    void revokeEndpoint(std::unique_ptr<Endpoint> /*endpoint*/) override {/*nop*/}
+    void addConfiguredDiscoveryFile(const std::string& path) override;
 
+    void removeConfiguredDiscoveryFile(const std::string& path) override;
 private:
+    celix::Properties convertToEndpointProperties(const rapidjson::Value &endpointJSON);
+    void readConfiguredDiscoveryFiles();
 
-    void discoverEndpoints();
+    const std::shared_ptr<celix::BundleContext> ctx;
+    const std::string configuredDiscoveryFiles;
 
-    std::shared_ptr<DependencyManager> _dependencyManager;
-    std::string _configurationFilePath;
+    std::mutex mutex{}; //protects below
+    std::unordered_map<std::string, std::vector<std::shared_ptr<celix::ServiceRegistration>>> endpointRegistrations{}; //key = configured discovery file path
 };
 
 } // end namespace celix::rsa.
diff --git a/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManagerActivator.cc b/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManagerActivator.cc
index e5758f6..ab61c8b 100644
--- a/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManagerActivator.cc
+++ b/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManagerActivator.cc
@@ -17,23 +17,18 @@
  * under the License.
  */
 
-#include <ConfiguredDiscoveryManagerActivator.h>
+#include "celix/BundleActivator.h"
 #include <ConfiguredDiscoveryManager.h>
 
-namespace {
-
-ConfiguredDiscoveryManagerActivator::ConfiguredDiscoveryManagerActivator(
-        const std::shared_ptr<celix::dm::DependencyManager>& dependencyManager) :
-            _component{dependencyManager->createComponent(
-                std::make_unique<celix::rsa::ConfiguredDiscoveryManager>(dependencyManager))
-                                        .addInterface<celix::rsa::IEndpointAnnouncer>().build()}, _mng(dependencyManager) {
-}
-
-    ConfiguredDiscoveryManagerActivator::~ConfiguredDiscoveryManagerActivator() {
-        _mng->destroyComponent(_component);
+class ConfiguredDiscoveryManagerActivator {
+public:
+    explicit ConfiguredDiscoveryManagerActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
+        auto &cmp = ctx->getDependencyManager()->createComponent(
+                std::make_unique<celix::rsa::ConfiguredDiscoveryManager>(ctx));
+        cmp.addInterface<celix::rsa::IEndpointAnnouncer>();
+        cmp.build();
     }
+};
 
 // define this class as the bundle activator.
 CELIX_GEN_CXX_BUNDLE_ACTIVATOR(ConfiguredDiscoveryManagerActivator)
-
-} // end anonymous namespace.
diff --git a/bundles/cxx_remote_services/discovery_configured/src/ConfiguredEndpoint.cc b/bundles/cxx_remote_services/discovery_configured/src/ConfiguredEndpoint.cc
deleted file mode 100644
index 0ded5c1..0000000
--- a/bundles/cxx_remote_services/discovery_configured/src/ConfiguredEndpoint.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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 <ConfiguredEndpoint.h>
-
-namespace celix::rsa {
-
-celix::dm::Properties convertEndpointPropertiesToCelix(const ConfiguredEndpointProperties& endpointProperties) {
-
-    return celix::dm::Properties{{celix::rsa::Endpoint::IMPORTED, std::to_string(endpointProperties.isImported()).c_str()},
-                                 {celix::rsa::Endpoint::EXPORTS, endpointProperties.getExports()},
-                                 {celix::rsa::Endpoint::IDENTIFIER, endpointProperties.getId()}};
-}
-
-ConfiguredEndpointProperties convertCelixPropertiesToEndpoint(const celix::dm::Properties& celixProperties) {
-
-    auto endpointId = celixProperties.get(celix::rsa::Endpoint::IDENTIFIER);
-    auto exports = celixProperties.get(celix::rsa::Endpoint::EXPORTS);
-    auto imported = celixProperties.get(celix::rsa::Endpoint::IMPORTED);
-    return ConfiguredEndpointProperties{endpointId,
-                                        (imported == "true"),
-                                        {}, exports, {}, "", ""};
-}
-
-bool isValidEndpointJson(const rapidjson::Value& endpointJson) {
-
-    return (endpointJson.HasMember(celix::rsa::Endpoint::IDENTIFIER)
-         && endpointJson.HasMember(celix::rsa::Endpoint::IMPORTED)
-         && endpointJson.HasMember(celix::rsa::Endpoint::IMPORT_CONFIGS)
-         && endpointJson.HasMember(celix::rsa::Endpoint::EXPORTS)
-         && endpointJson.HasMember(celix::rsa::Endpoint::OBJECTCLASS)
-         && endpointJson.HasMember(celix::rsa::Endpoint::SCOPE)
-         && endpointJson.HasMember(celix::rsa::Endpoint::TOPIC));
-}
-
-std::vector<std::string> parseJSONStringArray(const rapidjson::Value& jsonArray) {
-
-    std::vector<std::string> resultVec{};
-    if(jsonArray.IsArray()) {
-        resultVec.reserve(jsonArray.Size());
-        for(auto& element : jsonArray.GetArray()) {
-            if(element.IsString()) {
-                resultVec.emplace_back(element.GetString());
-            }
-        }
-    }
-    return resultVec;
-}
-
-ConfiguredEndpoint::ConfiguredEndpoint(const rapidjson::Value& endpointJson) :
-        Endpoint(celix::dm::Properties{}),
-        _configuredProperties{} {
-
-    if (isValidEndpointJson(endpointJson)) {
-
-        _configuredProperties = {endpointJson[celix::rsa::Endpoint::IDENTIFIER].GetString(),
-                                 endpointJson[celix::rsa::Endpoint::IMPORTED].GetBool(),
-                                 parseJSONStringArray(endpointJson[celix::rsa::Endpoint::IMPORT_CONFIGS]),
-                                 endpointJson[celix::rsa::Endpoint::EXPORTS].GetString(),
-                                 parseJSONStringArray(endpointJson[celix::rsa::Endpoint::OBJECTCLASS]),
-                                 endpointJson[celix::rsa::Endpoint::SCOPE].GetString(),
-                                 endpointJson[celix::rsa::Endpoint::TOPIC].GetString()};
-
-        _celixProperties = convertEndpointPropertiesToCelix(*_configuredProperties);
-    }
-}
-
-const ConfiguredEndpointProperties& ConfiguredEndpoint::getConfiguredProperties() const {
-
-    return *_configuredProperties;
-}
-
-} // end namespace celix::rsa.
diff --git a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/Endpoint.h b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/Endpoint.h
deleted file mode 100644
index 6035823..0000000
--- a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/Endpoint.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.
- */
-#pragma once
-
-#include <string>
-#include <map>
-
-#include <celix/dm/Properties.h>
-
-namespace celix::rsa {
-
-    /**
-     * @brief Endpoint class which represent a remote endpoint discovered by the framework.
-     */
-    class Endpoint {
-    public:
-        static constexpr const char * const IDENTIFIER = "endpoint.id";
-//        static constexpr const char * const IMPORTED = "service.imported";
-//        static constexpr const char * const IMPORT_CONFIGS = "service.imported.configs";
-        static constexpr const char * const EXPORTS = "service.exported.interfaces";
-//        static constexpr const char * const OBJECTCLASS = "endpoint.objectClass";
-//        static constexpr const char * const SCOPE = "endpoint.scope";
-//        static constexpr const char * const TOPIC = "endpoint.topic";
-
-
-        /**
-         * Endpoint constructor.
-         * @param properties celix properties with information about this endpoint and what its documenting.
-         */
-        explicit Endpoint(celix::Properties properties) : _celixProperties{std::move(properties)} {
-            // TODO validate mandatory properties are set.
-        }
-
-        [[nodiscard]] const celix::dm::Properties& getProperties() const {
-            return _celixProperties;
-        }
-
-        [[nodiscard]] std::string getEndpointId() const {
-            return _celixProperties.get(IDENTIFIER);
-        }
-
-        [[nodiscard]] std::string getExportedInterfaces() const {
-            return _celixProperties.get(EXPORTS);
-        }
-
-    protected:
-        celix::dm::Properties _celixProperties;
-
-    };
-} // end namespace celix::rsa.
diff --git a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/EndpointDescription.h b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/EndpointDescription.h
new file mode 100644
index 0000000..4b43142
--- /dev/null
+++ b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/EndpointDescription.h
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <string>
+#include <map>
+
+#include "celix/Constants.h"
+#include "celix/Properties.h"
+#include "celix/rsa/RemoteConstants.h"
+#include "celix/rsa/RemoteServicesException.h"
+
+namespace celix::rsa {
+
+    /**
+     * @brief A description of an endpoint that provides sufficient information for a compatible distribution provider to create a connection to this endpoint.
+     *
+     * An Endpoint Description is easy to transfer between different systems because it is property based where
+     * the property keys are strings and the values are simple types.
+     * This allows it to be used as a communications device to convey available endpoint information
+     * to nodes in a network. An Endpoint Description reflects the perspective of an importer.
+     * That is, the property keys have been chosen to match filters that are created by client bundles that need a service.
+     *
+     * Therefore the map must not contain any service.exported.* property and must contain the corresponding service.imported.* ones.
+     * The service.intents property must contain the intents provided by the service itself combined with the intents
+     * added by the exporting distribution provider. Qualified intents appear fully expanded on this property.
+     */
+    class EndpointDescription {
+    public:
+
+        /**
+         * @brief Create an Endpoint Description from properties
+         *
+         * The endpoint.id, service.imported.configs and objectClass properties must be set. .
+         *
+         * @param The properties from which to create the Endpoint Description.
+         */
+        explicit EndpointDescription(celix::Properties properties) : endpointProperties{std::move(properties)} {
+            checkValidEndpoint();
+        }
+
+        /**
+         * @brief Create an Endpoint Description based on service properties and optional provided rsa properties.
+         *
+         * The properties in the rsa properties take precedence over the service properties.
+         * the the service.imported property will be set.
+         * The endpoint.id, service.imported.configs and objectClass properties must be set after construction.
+         *
+         * @param frameworkUUID the framework uuid used for endpoint ids.
+         * @param serviceProperties The service properties of a service that can be exported.
+         * @param rsaProperties The optional properties provided by the Remote Service Admin.
+         */
+        EndpointDescription(std::string_view frameworkUUID, const celix::Properties& serviceProperties, const celix::Properties& rsaProperties = {}) : endpointProperties{
+                importedProperties(frameworkUUID, serviceProperties, rsaProperties)} {
+            checkValidEndpoint();
+        }
+
+        /**
+         * @brief Returns the endpoint's id.
+         *
+         * The id is an opaque id for an endpoint.
+         * No two different endpoints must have the same id.
+         * Two Endpoint Descriptions with the same id must represent the same endpoint.
+         * The value of the id is stored in the RemoteConstants.ENDPOINT_ID property.
+         */
+        [[nodiscard]] std::string getId() const {
+            return endpointProperties.get(celix::rsa::ENDPOINT_ID);
+        }
+
+        /**
+         * @brief Returns the configuration types.
+         *
+         * A distribution provider exports a service with an endpoint.
+         * This endpoint uses some kind of communications protocol with a set of configuration parameters.
+         * There are many different types but each endpoint is configured by only one configuration type.
+         * However, a distribution provider can be aware of different configuration types and provide synonyms to
+         * increase the change a receiving distribution provider can create a connection to this endpoint.
+         * This value of the configuration types is stored in the celix::rsa::SERVICE_IMPORTED_CONFIGS service property.
+         */
+        [[nodiscard]] std::string getConfigurationTypes() const {
+            return endpointProperties.get(celix::rsa::SERVICE_IMPORTED_CONFIGS);
+        }
+
+        /**
+         * @brief Return the framework UUID for the remote service, if present.
+         *
+         * The value of the remote framework UUID is stored in the celix::rsa::ENDPOINT_FRAMEWORK_UUID endpoint property.
+         */
+        [[nodiscard]] std::string getFrameworkUUID() const {
+            return endpointProperties.get(celix::rsa::ENDPOINT_FRAMEWORK_UUID);
+        }
+
+        /**
+         * @brief Return the list of intents implemented by this endpoint.
+         *
+         * The intents are based on the service.intents on an imported service, except for any intents that are
+         * additionally provided by the importing distribution provider.
+         * All qualified intents must have been expanded.
+         * This value of the intents is stored in the RemoteConstants.SERVICE_INTENTS service property.
+         */
+        //TODO make std::vector<std::string>
+        [[nodiscard]] std::string getIntents() const {
+            return endpointProperties.get(celix::rsa::SERVICE_INTENTS);
+        }
+
+        /**
+         * @brief  Provide the interface implemented by the exported service.
+         * The value of the interface is derived from the objectClass property.
+         */
+        [[nodiscard]] std::string getInterface() const {
+            return endpointProperties.get(celix::SERVICE_NAME);
+        }
+
+        /**
+         * @brief Returns all endpoint properties.
+         */
+        [[nodiscard]] const celix::Properties& getProperties() const {
+            return endpointProperties;
+        }
+
+        /**
+         * @brief converts the EndpointDescription to a printable string.
+         */
+        [[nodiscard]] std::string toString() const {
+            std::string result{"Endpoint["};
+            for (auto& prop : endpointProperties) {
+                result.append(prop.first);
+                result.append("=");
+                result.append(prop.second);
+                result.append(", ");
+            }
+            result.append("]");
+            return result;
+        }
+    protected:
+        celix::Properties importedProperties(std::string_view frameworkUUID, const celix::Properties& serviceProperties, const celix::Properties& rsaProperties) {
+            celix::Properties result{};
+
+            for (const auto& entry: serviceProperties) {
+                result.set(entry.first, entry.second); //TODO filter and remove service.export.* entries
+            }
+            for (const auto& entry: rsaProperties) {
+                result.set(entry.first, entry.second);
+            }
+
+            result.set(celix::rsa::SERVICE_IMPORTED, true);
+
+            if (result.get(celix::rsa::ENDPOINT_ID).empty()) {
+                result.set(celix::rsa::ENDPOINT_ID, std::string{frameworkUUID} + "-" + serviceProperties.get(celix::SERVICE_ID));
+            }
+
+            return result;
+        }
+
+        void checkValidEndpoint() const {
+            std::string baseMsg = "Invalid properties for EndpointDescription, missing mandatory property ";
+            if (endpointProperties.get(celix::rsa::ENDPOINT_ID).empty()) {
+                throw celix::rsa::RemoteServicesException{baseMsg.append(celix::rsa::ENDPOINT_ID)};
+            }
+            if (endpointProperties.get(celix::rsa::ENDPOINT_ID).empty()) {
+                throw celix::rsa::RemoteServicesException{baseMsg.append(celix::rsa::SERVICE_IMPORTED_CONFIGS)};
+            }
+            if (endpointProperties.get(celix::SERVICE_NAME).empty()) {
+                throw celix::rsa::RemoteServicesException{baseMsg.append(celix::SERVICE_NAME)};
+            }
+        }
+        
+        const celix::Properties endpointProperties;
+    };
+} // end namespace celix::rsa.
diff --git a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IEndpointAnnouncer.h b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IEndpointAnnouncer.h
index 425a6e4..6559b67 100644
--- a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IEndpointAnnouncer.h
+++ b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IEndpointAnnouncer.h
@@ -20,7 +20,7 @@
 
 #include <memory>
 
-#include "celix/rsa/Endpoint.h"
+#include "celix/rsa/EndpointDescription.h"
 #include "celix/dm/Properties.h"
 
 namespace celix::rsa {
@@ -39,13 +39,13 @@ public:
      * Task the endpoint announcer to make the given endpoint visible for discovery by other managers/ frameworks.
      * @param endpoint The endpoint pointer in question, with valid properties within.
      */
-    virtual void announceEndpoint(std::unique_ptr<Endpoint> endpoint) = 0;
+    virtual void announceEndpoint(std::unique_ptr<EndpointDescription> endpoint) = 0;
 
     /**
      * Task the endpoint announcer to remove the discoverability of a given endpoint.
      * @param endpoint The endpoint pointer in question, with valid properties within.
      */
-    virtual void revokeEndpoint(std::unique_ptr<Endpoint> endpoint) = 0;
+    virtual void revokeEndpoint(std::unique_ptr<EndpointDescription> endpoint) = 0;
 };
 
 } // end namespace celix::rsa.
diff --git a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IExportServiceFactory.h b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IExportServiceFactory.h
index 7b386fe..66383b6 100644
--- a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IExportServiceFactory.h
+++ b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IExportServiceFactory.h
@@ -19,7 +19,7 @@
 #pragma once
 
 #include <memory>
-#include "celix/rsa/Endpoint.h"
+#include "celix/rsa/EndpointDescription.h"
 
 namespace celix::rsa {
 
diff --git a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IExportedService.h b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IExportedService.h
index 50b3159..a7d1166 100644
--- a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IExportedService.h
+++ b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IExportedService.h
@@ -18,7 +18,7 @@
  */
 #pragma once
 
-#include "celix/rsa/Endpoint.h"
+#include "celix/rsa/EndpointDescription.h"
 
 namespace celix::rsa {
 
@@ -32,6 +32,6 @@ namespace celix::rsa {
         /**
          * @brief the endpoint which can be used to announce this exported service to the network.
          */
-        virtual std::shared_ptr<celix::rsa::Endpoint> getEndpoint() = 0;
+        virtual std::shared_ptr<celix::rsa::EndpointDescription> getEndpoint() = 0;
     };
 }
diff --git a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IImportServiceFactory.h b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IImportServiceFactory.h
index e3c15c2..7c6f3a1 100644
--- a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IImportServiceFactory.h
+++ b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IImportServiceFactory.h
@@ -19,7 +19,7 @@
 #pragma once
 
 #include <memory>
-#include "celix/rsa/Endpoint.h"
+#include "celix/rsa/EndpointDescription.h"
 
 namespace celix::rsa {
 
@@ -54,6 +54,6 @@ namespace celix::rsa {
          * @return A ImportService.
          * @throws celix::rsa::RemoteServicesException if the import failed.
          */
-        virtual std::unique_ptr<celix::rsa::IImportServiceGuard> importService(const celix::rsa::Endpoint& endpoint) = 0;
+        virtual std::unique_ptr<celix::rsa::IImportServiceGuard> importService(const celix::rsa::EndpointDescription& endpoint) = 0;
     };
 }
\ No newline at end of file
diff --git a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/RemoteConstants.h b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/RemoteConstants.h
new file mode 100644
index 0000000..9dae5e3
--- /dev/null
+++ b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/RemoteConstants.h
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+//TODO move header to rsa_api library
+
+namespace celix::rsa {
+
+
+    /**
+     * @brief Endpoint property identifying the universally unique id of the exporting framework.
+     *
+     * Can be absent if the corresponding endpoint is not for an OSGi service.
+     * The value of this property must be of type string.
+     */
+    constexpr const char * const ENDPOINT_FRAMEWORK_UUID = "endpoint.framework.uuid";
+
+    /**
+     * @brief Endpoint property identifying the id for this endpoint.
+     *
+     * This service property must always be set.
+     * The value of this property must be of type string.
+     */
+    constexpr const char * const ENDPOINT_ID = "endpoint.id";
+
+    /**
+     * @brief Endpoint property identifying the service id of the exported service.
+     *
+     * Can be absent or 0 if the corresponding endpoint is not for an OSGi service.
+     * The value of this property must be of type long.
+     */
+    constexpr const char * const ENDPOINT_SERVICE_ID = "endpoint.service.id";
+
+    /**
+     * @brief Service property identifying the configuration types supported by a distribution provider.
+     *
+     * Registered by the distribution provider on one of its services to indicate the supported configuration types.
+     * The value of this property must be of type string and can be a ',' separated list.
+     */
+    constexpr const char * const REMOTE_CONFIGS_SUPPORTED = "remote.configs.supported";
+
+    /**
+     * @brief Service property identifying the configuration types that should be used to export the service.
+     *
+     * Each configuration type represents the configuration parameters for an endpoint.
+     * A distribution provider should create an endpoint for each configuration type that it supports.
+     * This property may be supplied in the properties object passed to a registerService method.
+     * The value of this property must be of type string and can be a ',' separated list.
+     */
+    constexpr const char * const REMOTE_INTENTS_SUPPORTED = "remote.intents.supported";
+
+    /**
+     * @brief  Service property identifying the intents that the distribution provider must implement to distribute the service.
+     *
+     * Intents listed in this property are reserved for intents that are critical for the code to function correctly,
+     * for example, ordering of messages. These intents should not be configurable.
+     * This property may be supplied in the properties object passed to the a registerService method.
+     * The value of this property must be of type string and can be a ',' separated list.
+     */
+    constexpr const char * const SERVICE_EXPORTED_INTENTS = "service.exported.intents";
+
+    /**
+     * @brief Service property identifying the extra intents that the distribution provider must implement to distribute the service.
+     *
+     * This property is merged with the service.exported.intents property before the distribution provider interprets
+     * the listed intents; it has therefore the same semantics but the property should be configurable so the
+     * administrator can choose the intents based on the topology.
+     * Bundles should therefore make this property configurable, for example through the Configuration Admin service.
+     * This property may be supplied in the properties object passed to the a registerService method.
+     * The value of this property must be of type string and can be a ',' separated list.
+     */
+    constexpr const char * const SERVICE_EXPORTED_INTENTS_EXTRA = "service.exported.intents.extra";
+
+    /**
+     * @brief Service property marking the service for export. It defines the interfaces under which this service can be exported.
+     *
+     * Note for Celix only 1 interface can be register per service regiration, so only 1 interface can be exported using
+     * the service.exported.interfaces property.
+     * This value must be the exported service type or the value of an asterisk ('*' \u002A).
+     * The value of this property must be of type string.
+     */
+    constexpr const char * const SERVICE_EXPORTED_INTERFACES = "service.exported.interfaces";
+
+    /**
+     * @brief Service property identifying the service as imported.
+     *
+     * This service property must be set by a distribution provider to any value when it registers the endpoint proxy as an imported service.
+     * A bundle can use this property to filter out imported services. The value of this property may be of any type.
+     */
+    constexpr const char * const SERVICE_IMPORTED = "service.imported";
+
+    /**
+     * @brief Service property identifying the configuration types used to import the service.
+     *
+     * Any associated properties for this configuration types must be properly mapped to the importing system.
+     * For example, a URL in these properties must point to a valid resource when used in the importing framework.
+     * If multiple configuration types are listed in this property, then they must be synonyms for exactly the same
+     * remote endpoint that is used to export this service.
+     * The value of this property must be of type string and can be a ',' separated list.
+     */
+    constexpr const char * const SERVICE_IMPORTED_CONFIGS = "service.imported.configs";
+
+    /**
+     * @brief Service property identifying the intents that this service implement.
+     *
+     * This property has a dual purpose:
+     * A bundle can use this service property to notify the distribution provider that these intents are already
+     * implemented by the exported service object.
+     * A distribution provider must use this property to convey the combined intents of: The exporting service,
+     * and the intents that the exporting distribution provider adds, and the intents that the importing distribution provider adds.
+     * To export a service, a distribution provider must expand any qualified intents. Both the exporting and
+     * importing distribution providers must recognize all intents before a service can be distributed.
+     * The value of this property must be of type string and can be a ',' separated list.
+     */
+    constexpr const char * const SERVICE_INTENTS = "service.intents";
+}
\ No newline at end of file
diff --git a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/RemoteServicesException.h b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/RemoteServicesException.h
index bc30a1f..e9a6b0c 100644
--- a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/RemoteServicesException.h
+++ b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/RemoteServicesException.h
@@ -9,7 +9,7 @@ namespace celix::rsa {
      */
     class RemoteServicesException : public std::exception {
     public:
-        explicit Exception(std::string msg) : w{std::move(msg)} {}
+        explicit RemoteServicesException(std::string msg) : w{std::move(msg)} {}
 
         const char* what() const noexcept override {
             return w.c_str();
diff --git a/libs/framework/gtest/CMakeLists.txt b/libs/framework/gtest/CMakeLists.txt
index e1045b7..178735a 100644
--- a/libs/framework/gtest/CMakeLists.txt
+++ b/libs/framework/gtest/CMakeLists.txt
@@ -43,6 +43,7 @@ add_executable(test_framework
         src/HelloWorldCxxActivator.cc
         src/CxxFilterTestSuite.cc
         src/CxxFrameworkFactoryTestSuite.cc
+        src/CxxUtilsTestSuite.cc
 )
 
 target_link_libraries(test_framework Celix::framework CURL::libcurl GTest::gtest GTest::gtest_main)
diff --git a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc
index ce019aa..8f059fd 100644
--- a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc
+++ b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc
@@ -104,7 +104,6 @@ TEST_F(CxxBundleContextTestSuite, RegisterCServiceTest) {
     auto svcReg = ctx->registerService<CInterface>(svc).build();
     svcReg->wait();
 
-    std::cout << "Name is " << celix::typeName<CInterface>() << std::endl;
     long svcId = ctx->findService<CInterface>();
     EXPECT_GE(svcId, 0L);
 
diff --git a/libs/framework/gtest/src/CxxUtilsTestSuite.cc b/libs/framework/gtest/src/CxxUtilsTestSuite.cc
new file mode 100644
index 0000000..28d8594
--- /dev/null
+++ b/libs/framework/gtest/src/CxxUtilsTestSuite.cc
@@ -0,0 +1,62 @@
+/*
+ * 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/Utils.h"
+
+class CxxUtilsTestSuite : public ::testing::Test {
+public:
+};
+
+namespace example {
+    class TestType {
+
+    };
+}
+
+
+TEST_F(CxxUtilsTestSuite, TypenameTest) {
+    auto name = celix::typeName<example::TestType>();
+    EXPECT_FALSE(name.empty());
+}
+
+TEST_F(CxxUtilsTestSuite, SplitTest) {
+    auto tokens = celix::split("item1,item2,item3");
+    ASSERT_EQ(tokens.size(), 3);
+    EXPECT_EQ(tokens[0], "item1");
+    EXPECT_EQ(tokens[1], "item2");
+    EXPECT_EQ(tokens[2], "item3");
+
+    tokens = celix::split("  item1 , item2  ,  item3  ");
+    ASSERT_EQ(tokens.size(), 3);
+    EXPECT_EQ(tokens[0], "item1");
+    EXPECT_EQ(tokens[1], "item2");
+    EXPECT_EQ(tokens[2], "item3");
+
+    tokens = celix::split("  item1 , ");
+    ASSERT_EQ(tokens.size(), 1);
+    EXPECT_EQ(tokens[0], "item1");
+
+    tokens = celix::split("");
+    EXPECT_EQ(tokens.size(), 0);
+
+    tokens = celix::split("  , ,   ");
+    EXPECT_EQ(tokens.size(), 0);
+}
\ No newline at end of file
diff --git a/libs/framework/include/celix/Utils.h b/libs/framework/include/celix/Utils.h
index bf4be75..db00a03 100644
--- a/libs/framework/include/celix/Utils.h
+++ b/libs/framework/include/celix/Utils.h
@@ -23,6 +23,7 @@
 #include <string>
 #include <string.h>
 #include <iostream>
+#include <vector>
 
 //NOTE based on has_rtti.cpp
 #if defined(__clang__)
@@ -97,6 +98,25 @@ namespace celix {
     std::string typeName(const std::string& providedTypeName) {
         return providedTypeName.empty() ? celix::typeName<I>() : providedTypeName;
     }
+
+    /**
+     * @brief Splits a string using the provided delim.
+     *
+     * Also trims the entries from whitespaces.
+     * @param str The string to split
+     * @param delimiter The delimiter to use (default ",")
+     */
+     inline std::vector<std::string> split(const std::string& str, const std::string& delimiter = ",") {
+        std::vector<std::string> result{};
+        std::string delimiters = delimiter + " \t";
+        size_t found;
+        size_t pos = 0;
+        while ((found = str.find_first_not_of(delimiters, pos)) != std::string::npos) {
+            pos = str.find_first_of(", ", found);
+            result.emplace_back(str.substr(found, pos - found));
+        }
+        return result;
+     }
 }
 
 #undef CELIX_RTTI_ENABLED
\ No newline at end of file