Merge pull request #347 from apache/feature/add_intent_to_cxx_rsa

Feature/add intent to cxx rsa
diff --git a/bundles/cxx_remote_services/admin/CMakeLists.txt b/bundles/cxx_remote_services/admin/CMakeLists.txt
index 4a6dac3..24b77ab 100644
--- a/bundles/cxx_remote_services/admin/CMakeLists.txt
+++ b/bundles/cxx_remote_services/admin/CMakeLists.txt
@@ -23,8 +23,8 @@
         FILENAME "Celix_RemoteServiceAdmin"
         SOURCES
             src/RemoteServiceAdmin.cc
+            src/RemoteServiceAdminActivator.cc
 )
-target_include_directories(RemoteServiceAdmin PRIVATE include)
 target_link_libraries(RemoteServiceAdmin PRIVATE
         Celix::rsa_spi
         Celix::framework
diff --git a/bundles/cxx_remote_services/admin/gtest/src/RemoteServiceAdminTestSuite.cc b/bundles/cxx_remote_services/admin/gtest/src/RemoteServiceAdminTestSuite.cc
index 3ffdd18..d9b00cf 100644
--- a/bundles/cxx_remote_services/admin/gtest/src/RemoteServiceAdminTestSuite.cc
+++ b/bundles/cxx_remote_services/admin/gtest/src/RemoteServiceAdminTestSuite.cc
@@ -87,10 +87,10 @@
     std::string cmpUUID{};
 };
 
-class StubExportServiceGuard : public celix::rsa::IExportServiceGuard {
+class StubExportRegistration : public celix::rsa::IExportRegistration {
 public:
-    explicit StubExportServiceGuard(std::weak_ptr<StubExportedServiceEntry> _entry) : entry{std::move(_entry)} {}
-    ~StubExportServiceGuard() noexcept override {
+    explicit StubExportRegistration(std::weak_ptr<StubExportedServiceEntry> _entry) : entry{std::move(_entry)} {}
+    ~StubExportRegistration() noexcept override {
         auto e = entry.lock();
         if (e) {
             e->close();
@@ -101,28 +101,45 @@
     const std::weak_ptr<StubExportedServiceEntry> entry;
 };
 
-class StubExportServiceFactory : public celix::rsa::IExportServiceFactory {
-public:
-    explicit StubExportServiceFactory(std::shared_ptr<celix::BundleContext> _ctx) : ctx{std::move(_ctx)} {}
-
-    std::unique_ptr<celix::rsa::IExportServiceGuard> exportService(const celix::Properties& /*serviceProperties*/) override {
-        auto entry = std::make_shared<StubExportedServiceEntry>(ctx);
-        std::lock_guard<std::mutex> lock{mutex};
-        entries.emplace_back(entry);
-        return std::make_unique<StubExportServiceGuard>(entry);
-    }
-
-private:
-    const std::shared_ptr<celix::BundleContext> ctx;
-    std::mutex mutex{};
-    std::vector<std::shared_ptr<StubExportedServiceEntry>> entries{};
-};
-
 class IDummyService {
 public:
     virtual ~IDummyService() noexcept = default;
 };
 
+class StubExportServiceFactory : public celix::rsa::IExportServiceFactory {
+public:
+    explicit StubExportServiceFactory(std::shared_ptr<celix::BundleContext> _ctx) : ctx{std::move(_ctx)} {}
+    ~StubExportServiceFactory() noexcept override = default;
+
+    std::unique_ptr<celix::rsa::IExportRegistration> exportService(const celix::Properties& /*serviceProperties*/) override {
+        auto entry = std::make_shared<StubExportedServiceEntry>(ctx);
+        std::lock_guard<std::mutex> lock{mutex};
+        entries.emplace_back(entry);
+        return std::make_unique<StubExportRegistration>(entry);
+    }
+
+
+    [[nodiscard]] const std::string &getRemoteServiceType() const override {
+        return serviceType;
+    }
+
+    [[nodiscard]] const std::vector<std::string> &getSupportedIntents() const override {
+        return intents;
+    }
+
+    [[nodiscard]] const std::vector<std::string> &getSupportedConfigs() const override {
+        return configs;
+    }
+
+private:
+    const std::string serviceType = celix::typeName<IDummyService>();
+    const std::vector<std::string> configs = {"test"};
+    const std::vector<std::string> intents = {"osgi.basic"};
+    const std::shared_ptr<celix::BundleContext> ctx;
+    std::mutex mutex{};
+    std::vector<std::shared_ptr<StubExportedServiceEntry>> entries{};
+};
+
 class DummyServiceImpl : public IDummyService {
 public:
     ~DummyServiceImpl() noexcept override = default;
@@ -141,7 +158,7 @@
      EXPECT_EQ(0, count);
 
      auto reg1 = ctx->registerService<celix::rsa::IExportServiceFactory>(std::make_shared<StubExportServiceFactory>(ctx))
-             .addProperty(celix::rsa::IExportServiceFactory::TARGET_SERVICE_NAME, celix::typeName<IDummyService>())
+             .addProperty(celix::rsa::IExportServiceFactory::REMOTE_SERVICE_TYPE, celix::typeName<IDummyService>())
              .build();
 
     count = ctx->useService<celix::rsa::IExportedService>()
@@ -190,7 +207,7 @@
     EXPECT_EQ(0, count);
 
     auto reg2 = ctx->registerService<celix::rsa::IExportServiceFactory>(std::make_shared<StubExportServiceFactory>(ctx))
-            .addProperty(celix::rsa::IExportServiceFactory::TARGET_SERVICE_NAME, celix::typeName<IDummyService>())
+            .addProperty(celix::rsa::IExportServiceFactory::REMOTE_SERVICE_TYPE, celix::typeName<IDummyService>())
             .build();
 
     //rsa called export service factory which created a IExportServiceRegistration, which register the marker interface IExportedService indicating an exported service
@@ -199,7 +216,38 @@
     EXPECT_EQ(1, count);
 }
 
+TEST_F(RemoteServiceAdminTestSuite, exportServiceUsingConfig) {
+    auto bndId = ctx->installBundle(REMOTE_SERVICE_ADMIN_BUNDLE_LOCATION);
+    EXPECT_GE(bndId, 0);
 
+    auto factoryRegistration = ctx->registerService<celix::rsa::IExportServiceFactory>(std::make_shared<StubExportServiceFactory>(ctx))
+            .addProperty(celix::rsa::IExportServiceFactory::REMOTE_SERVICE_TYPE, celix::typeName<IDummyService>())
+            .build();
+
+    /**
+     * When I add a export service factory and register a service with exported interfaces and a mismatched
+     * service.export.configs this will no be exported.
+     */
+    auto reg = ctx->registerService<IDummyService>(std::make_shared<DummyServiceImpl>())
+            .addProperty(celix::rsa::SERVICE_EXPORTED_INTERFACES, "*")
+            .addProperty(celix::rsa::SERVICE_IMPORTED_CONFIGS, "non-existing-config")
+            .build();
+    auto count = ctx->useService<celix::rsa::IExportedService>()
+            .build();
+    EXPECT_EQ(0, count);
+
+    /**
+     * When I add a export service factory and register a service with exported interfaces and a correct
+     * service.export.configs the service will be exported
+     */
+    reg = ctx->registerService<IDummyService>(std::make_shared<DummyServiceImpl>())
+            .addProperty(celix::rsa::SERVICE_EXPORTED_INTERFACES, "*")
+            .addProperty(celix::rsa::SERVICE_IMPORTED_CONFIGS, "test")
+            .build();
+    count = ctx->useService<celix::rsa::IExportedService>()
+            .build();
+    EXPECT_EQ(1, count);
+}
 
 
 /**
@@ -237,10 +285,10 @@
     std::string cmpUUID{};
 };
 
-class StubImportServiceGuard : public celix::rsa::IImportServiceGuard {
+class StubImportRegistration : public celix::rsa::IImportRegistration {
 public:
-    explicit StubImportServiceGuard(std::weak_ptr<StubImportedServiceEntry> _entry) : entry{std::move(_entry)} {}
-    ~StubImportServiceGuard() noexcept override {
+    explicit StubImportRegistration(std::weak_ptr<StubImportedServiceEntry> _entry) : entry{std::move(_entry)} {}
+    ~StubImportRegistration() noexcept override {
         auto e = entry.lock();
         if (e) {
             e->close();
@@ -254,19 +302,30 @@
 class StubImportServiceFactory : public celix::rsa::IImportServiceFactory {
 public:
     explicit StubImportServiceFactory(std::shared_ptr<celix::BundleContext> _ctx) : ctx{std::move(_ctx)} {}
+    ~StubImportServiceFactory() noexcept override = default;
 
-    virtual std::unique_ptr<celix::rsa::IImportServiceGuard> importService(const celix::rsa::EndpointDescription& endpoint) {
+    [[nodiscard]] std::unique_ptr<celix::rsa::IImportRegistration> importService(const celix::rsa::EndpointDescription& endpoint) override {
            if (endpoint.getInterface() == celix::typeName<IDummyService>()) {
                std::lock_guard<std::mutex> lock{mutex};
                auto entry = std::make_shared<StubImportedServiceEntry>(ctx);
                entries.emplace_back(entry);
-               return std::make_unique<StubImportServiceGuard>(entry);
+               return std::make_unique<StubImportRegistration>(entry);
            } else {
                return {};
            }
     }
 
+    [[nodiscard]] const std::string &getRemoteServiceType() const override {
+        return serviceType;
+    }
+
+    [[nodiscard]] const std::vector<std::string> &getSupportedConfigs() const override {
+        return configs;
+    }
+
 private:
+    const std::string serviceType{celix::typeName<IDummyService>()};
+    const std::vector<std::string> configs{"test"};
     const std::shared_ptr<celix::BundleContext> ctx;
     std::mutex mutex{};
     std::vector<std::shared_ptr<StubImportedServiceEntry>> entries{};
@@ -285,7 +344,7 @@
     EXPECT_EQ(0, count);
 
     auto reg1 = ctx->registerService<celix::rsa::IImportServiceFactory>(std::make_shared<StubImportServiceFactory>(ctx))
-            .addProperty(celix::rsa::IImportServiceFactory::TARGET_SERVICE_NAME, celix::typeName<IDummyService>())
+            .addProperty(celix::rsa::IImportServiceFactory::REMOTE_SERVICE_TYPE, celix::typeName<IDummyService>())
             .build();
 
     count = ctx->useService<IDummyService>()
@@ -294,7 +353,8 @@
 
     auto endpoint = std::make_shared<celix::rsa::EndpointDescription>(celix::Properties{
         {celix::rsa::ENDPOINT_ID, "endpoint-id-1"},
-        {celix::SERVICE_NAME,celix::typeName<IDummyService>()}});
+        {celix::SERVICE_NAME,celix::typeName<IDummyService>()},
+        {celix::rsa::SERVICE_IMPORTED_CONFIGS, "test"}});
     auto reg2 = ctx->registerService<celix::rsa::EndpointDescription>(std::move(endpoint))
             .build();
 
diff --git a/bundles/cxx_remote_services/admin/src/RemoteServiceAdmin.cc b/bundles/cxx_remote_services/admin/src/RemoteServiceAdmin.cc
index 207feec..116820c 100644
--- a/bundles/cxx_remote_services/admin/src/RemoteServiceAdmin.cc
+++ b/bundles/cxx_remote_services/admin/src/RemoteServiceAdmin.cc
@@ -19,21 +19,20 @@
 
 #include "RemoteServiceAdmin.h"
 #include "celix/BundleContext.h"
-#include "celix/BundleActivator.h"
 #include "celix/rsa/RemoteConstants.h"
-#include "celix/rsa/IImportServiceFactory.h"
-#include "celix/rsa/IExportServiceFactory.h"
 
+#define L_TRACE(...) \
+        logHelper.trace(__VA_ARGS__);
 #define L_DEBUG(...) \
-        _logHelper.debug(__VA_ARGS__);
+        logHelper.debug(__VA_ARGS__);
 #define L_INFO(...) \
         _logHelper.info(__VA_ARGS__);
 #define L_WARN(...) \
-        _logHelper.warning(__VA_ARGS__);
+        logHelper.warning(__VA_ARGS__);
 #define L_ERROR(...) \
-        _logHelper.error(__VA_ARGS__);
+        logHelper.error(__VA_ARGS__);
 
-celix::rsa::RemoteServiceAdmin::RemoteServiceAdmin(celix::LogHelper logHelper) : _logHelper{std::move(logHelper)} {}
+celix::rsa::RemoteServiceAdmin::RemoteServiceAdmin(celix::LogHelper logHelper) : logHelper{std::move(logHelper)} {}
 
 void celix::rsa::RemoteServiceAdmin::addEndpoint(const std::shared_ptr<celix::rsa::EndpointDescription>& endpoint) {
     assert(endpoint);
@@ -50,8 +49,8 @@
         return;
     }
 
-    std::lock_guard l(_m);
-    _toBeImportedServices.emplace_back(endpoint);
+    std::lock_guard l(mutex);
+    toBeImportedServices.emplace_back(endpoint);
     createImportServices();
 }
 
@@ -64,18 +63,18 @@
         return;
     }
 
-    std::shared_ptr<celix::rsa::IImportServiceGuard> tmpStore{}; //to ensure destruction outside of lock
+    std::shared_ptr<celix::rsa::IImportRegistration> tmpStore{}; //to ensure destruction outside of lock
     {
-        std::lock_guard l(_m);
+        std::lock_guard l(mutex);
 
-        _toBeImportedServices.erase(std::remove_if(_toBeImportedServices.begin(), _toBeImportedServices.end(), [&id](auto const &endpoint){
+        toBeImportedServices.erase(std::remove_if(toBeImportedServices.begin(), toBeImportedServices.end(), [&id](auto const &endpoint){
             return id == endpoint->getId();
-        }), _toBeImportedServices.end());
+        }), toBeImportedServices.end());
 
-        auto it = _importedServices.find(id);
-        if (it != _importedServices.end()) {
+        auto it = importedServices.find(id);
+        if (it != importedServices.end()) {
             tmpStore = std::move(it->second);
-            _importedServices.erase(it);
+            importedServices.erase(it);
         }
     }
 }
@@ -83,21 +82,21 @@
 void celix::rsa::RemoteServiceAdmin::addImportedServiceFactory(
         const std::shared_ptr<celix::rsa::IImportServiceFactory> &factory,
         const std::shared_ptr<const celix::Properties> &properties) {
-    auto targetServiceName = properties->get(celix::rsa::IImportServiceFactory::TARGET_SERVICE_NAME);
+    auto targetServiceName = properties->get(celix::rsa::IImportServiceFactory::REMOTE_SERVICE_TYPE);
     if (targetServiceName.empty()) {
-        L_WARN("Adding service factory but missing %s property", celix::rsa::IImportServiceFactory::TARGET_SERVICE_NAME);
+        L_WARN("Adding service factory but missing %s property", celix::rsa::IImportServiceFactory::REMOTE_SERVICE_TYPE);
         return;
     }
 
 
-    std::lock_guard l(_m);
-    auto existingFactory = _importServiceFactories.find(targetServiceName);
-    if (existingFactory != end(_importServiceFactories)) {
+    std::lock_guard l(mutex);
+    auto existingFactory = importServiceFactories.find(targetServiceName);
+    if (existingFactory != end(importServiceFactories)) {
         L_WARN("Adding imported factory but factory already exists");
         return;
     }
 
-    _importServiceFactories.emplace(targetServiceName, factory);
+    importServiceFactories.emplace(targetServiceName, factory);
 
     createImportServices();
 }
@@ -105,14 +104,14 @@
 void celix::rsa::RemoteServiceAdmin::removeImportedServiceFactory(
         const std::shared_ptr<celix::rsa::IImportServiceFactory> &/*factory*/,
         const std::shared_ptr<const celix::Properties> &properties) {
-    auto targetServiceName = properties->get(celix::rsa::IImportServiceFactory::TARGET_SERVICE_NAME);
+    auto targetServiceName = properties->get(celix::rsa::IImportServiceFactory::REMOTE_SERVICE_TYPE);
     if (targetServiceName.empty()) {
-        L_WARN("Removing service factory but missing %s property", celix::rsa::IImportServiceFactory::TARGET_SERVICE_NAME);
+        L_WARN("Removing service factory but missing %s property", celix::rsa::IImportServiceFactory::REMOTE_SERVICE_TYPE);
         return;
     }
 
-    std::lock_guard l(_m);
-    _importServiceFactories.erase(targetServiceName);
+    std::lock_guard l(mutex);
+    importServiceFactories.erase(targetServiceName);
 
     //TODO remove imported services from this factory ??needed
 }
@@ -120,28 +119,28 @@
 void celix::rsa::RemoteServiceAdmin::addExportedServiceFactory(
         const std::shared_ptr<celix::rsa::IExportServiceFactory> &factory,
         const std::shared_ptr<const celix::Properties> &properties) {
-    auto targetServiceName = properties->get(celix::rsa::IExportServiceFactory::TARGET_SERVICE_NAME);
+    auto targetServiceName = properties->get(celix::rsa::IExportServiceFactory::REMOTE_SERVICE_TYPE);
     if (targetServiceName.empty()) {
-        L_WARN("Adding service factory but missing %s property", celix::rsa::IExportServiceFactory::TARGET_SERVICE_NAME);
+        L_WARN("Adding service factory but missing %s property", celix::rsa::IExportServiceFactory::REMOTE_SERVICE_TYPE);
         return;
     }
 
-    std::lock_guard<std::mutex> lock{_m};
-    _exportServiceFactories.emplace(targetServiceName, factory);
+    std::lock_guard<std::mutex> lock{mutex};
+    exportServiceFactories.emplace(targetServiceName, factory);
     createExportServices();
 }
 
 void celix::rsa::RemoteServiceAdmin::removeExportedServiceFactory(
         const std::shared_ptr<celix::rsa::IExportServiceFactory> &/*factory*/,
         const std::shared_ptr<const celix::Properties> &properties) {
-    auto targetServiceName = properties->get(celix::rsa::IExportServiceFactory::TARGET_SERVICE_NAME);
+    auto targetServiceName = properties->get(celix::rsa::IExportServiceFactory::REMOTE_SERVICE_TYPE);
     if (targetServiceName.empty()) {
-        L_WARN("Removing service factory but missing %s property", celix::rsa::IExportServiceFactory::TARGET_SERVICE_NAME);
+        L_WARN("Removing service factory but missing %s property", celix::rsa::IExportServiceFactory::REMOTE_SERVICE_TYPE);
         return;
     }
 
-    std::lock_guard l(_m);
-    _exportServiceFactories.erase(targetServiceName);
+    std::lock_guard l(mutex);
+    exportServiceFactories.erase(targetServiceName);
 
     //TODO remove exported services from this factory ??needed
 }
@@ -153,8 +152,8 @@
         return;
     }
 
-    std::lock_guard<std::mutex> lock{_m};
-    _toBeExportedServices.emplace_back(props);
+    std::lock_guard<std::mutex> lock{mutex};
+    toBeExportedServices.emplace_back(props);
     createExportServices();
 }
 
@@ -165,94 +164,125 @@
         return;
     }
 
-    std::lock_guard l(_m);
+    std::lock_guard l(mutex);
 
-    auto instanceIt = _exportedServices.find(svcId);
-    if (instanceIt != end(_exportedServices)) {
-        _exportedServices.erase(instanceIt);
+    auto instanceIt = exportedServices.find(svcId);
+    if (instanceIt != end(exportedServices)) {
+        exportedServices.erase(instanceIt);
     }
 
     //remove to be exported endpoint (if present)
-    for (auto it = _toBeExportedServices.begin(); it != _toBeExportedServices.end(); ++it) {
+    for (auto it = toBeExportedServices.begin(); it != toBeExportedServices.end(); ++it) {
         if ((*it)->getAsLong(celix::SERVICE_ID, -1) == svcId) {
-            _toBeExportedServices.erase(it);
+            toBeExportedServices.erase(it);
             break;
         }
     }
 }
 
-void celix::rsa::RemoteServiceAdmin::createImportServices() {
-    auto it = _toBeImportedServices.begin();
-    while (it != _toBeImportedServices.end()) {
-        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;
+bool celix::rsa::RemoteServiceAdmin::isEndpointMatch(const celix::rsa::EndpointDescription& endpoint, const celix::rsa::IImportServiceFactory& factory) const {
+    if (factory.getSupportedConfigs().empty()) {
+        L_WARN("Matching endpoint with a import service factory with no supported configs, this will always fail");
+        return false;
+    }
+    if (endpoint.getConfigurationTypes().empty()) {
+        L_WARN("Matching endpoint with empty configuration types (%s), this will always fail", celix::rsa::SERVICE_IMPORTED_CONFIGS);
+        return false;
+    }
+    size_t matchCount = 0;
+    for (const auto& config : endpoint.getConfigurationTypes()) {
+        for (const auto& factoryConfig : factory.getSupportedConfigs()) {
+            if (config == factoryConfig) {
+                ++matchCount;
+                break;
+            }
         }
-        auto endpointId = (*it)->getId();
-        L_DEBUG("Adding endpoint, created service");
-        _importedServices.emplace(endpointId, existingFactory->second->importService(**it));
-        it = _toBeImportedServices.erase(it);
+    }
+    return matchCount == factory.getSupportedConfigs().size(); //note must match all requested configurations
+}
+
+bool celix::rsa::RemoteServiceAdmin::isExportServiceMatch(const celix::Properties& svcProperties, const celix::rsa::IExportServiceFactory& factory) const {
+    auto providedConfigs = celix::split(svcProperties.get(celix::rsa::SERVICE_IMPORTED_CONFIGS));
+    auto providedIntents = celix::split(svcProperties.get(celix::rsa::SERVICE_EXPORTED_INTENTS, "osgi.basic"));
+    if (providedConfigs.empty() && providedIntents.empty()) {
+        //note cannot match export service with no config or intent
+        return false;
+    }
+    if (factory.getSupportedIntents().empty() && factory.getSupportedConfigs().empty()) {
+        L_WARN("Matching service marked for export with a export service factory with no supported intents or configs, this will always fail");
+        return false;
+    }
+    if (!providedConfigs.empty()) {
+        size_t matchCount = 0;
+        for (const auto& config : providedConfigs) {
+            for (const auto& factoryConfig : factory.getSupportedConfigs()) {
+                if (config == factoryConfig) {
+                    ++matchCount;
+                    break;
+                }
+            }
+        }
+        return matchCount == factory.getSupportedConfigs().size(); //note must match all requested configurations
+    } else /*match on intent*/ {
+        for (const auto& intent: providedIntents) {
+            for (const auto& factoryIntent: factory.getSupportedIntents()) {
+                if (intent == factoryIntent) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
+
+void celix::rsa::RemoteServiceAdmin::createImportServices() {
+    //precondition mutex taken
+    auto it = toBeImportedServices.begin();
+    while (it != toBeImportedServices.end()) {
+        auto interface = (*it)->getInterface();
+        bool match = false;
+        for (auto factoryIt = importServiceFactories.lower_bound(interface); factoryIt != importServiceFactories.end() && factoryIt->first == interface; ++factoryIt) {
+            if (isEndpointMatch(**it, *factoryIt->second)) {
+                match = true;
+                auto endpointId = (*it)->getId();
+                L_DEBUG("Adding endpoint %s, created export service for %s", endpointId.c_str(), (*it)->getInterface().c_str());
+                importedServices.emplace(endpointId, factoryIt->second->importService(**it));
+                break;
+            } else {
+                L_TRACE("Config mismatch.");
+            }
+        }
+        if (match) {
+            it = toBeImportedServices.erase(it);
+        } else {
+            L_DEBUG("Adding endpoint to be imported but no matching factory available yet, delaying import");
+            ++it;
+        }
     }
 }
 
 void celix::rsa::RemoteServiceAdmin::createExportServices() {
-    auto it = _toBeExportedServices.begin();
-    while (it != _toBeExportedServices.end()) {
+    //precondition mutex taken
+    auto it = toBeExportedServices.begin();
+    while (it != toBeExportedServices.end()) {
         const auto& svcProperties = **it;
         auto serviceName = svcProperties.get(celix::SERVICE_NAME, "");
         auto svcId = svcProperties.getAsLong(celix::SERVICE_ID, -1);
-        if (serviceName.empty()) {
-            L_WARN("Adding service to be exported but missing objectclass for svc id %li", svcId);
-            it++;
-            continue;
+        bool match = false;
+        for (auto factoryIt = exportServiceFactories.lower_bound(serviceName); factoryIt != exportServiceFactories.end() && factoryIt->first == serviceName; ++factoryIt) {
+            if (isExportServiceMatch(svcProperties, *factoryIt->second)) {
+                match = true;
+                exportedServices.emplace(svcId, factoryIt->second->exportService(svcProperties));
+                break;
+            } else {
+                L_TRACE("Intent mismatch");
+            }
         }
-        auto factory = _exportServiceFactories.find(serviceName);
-        if (factory == end(_exportServiceFactories)) {
-            L_DEBUG("Adding service to be exported but no factory available yet, delaying creation");
-            it++;
-            continue;
+        if (match) {
+            it = toBeExportedServices.erase(it);
+        } else {
+            L_DEBUG("Adding endpoint to be imported but no matching factory available yet, delaying import");
+            ++it;
         }
-        _exportedServices.emplace(svcId, factory->second->exportService(svcProperties));
-        it = _toBeExportedServices.erase(it);
     }
-}
-
-class AdminActivator {
-public:
-    explicit AdminActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
-        auto admin = std::make_shared<celix::rsa::RemoteServiceAdmin>(celix::LogHelper{ctx, celix::typeName<celix::rsa::RemoteServiceAdmin>()});
-
-        auto& cmp = ctx->getDependencyManager()->createComponent(admin);
-        cmp.createServiceDependency<celix::rsa::EndpointDescription>()
-                .setRequired(false)
-                .setStrategy(celix::dm::DependencyUpdateStrategy::locking)
-                .setCallbacks(&celix::rsa::RemoteServiceAdmin::addEndpoint, &celix::rsa::RemoteServiceAdmin::removeEndpoint);
-        cmp.createServiceDependency<celix::rsa::IImportServiceFactory>()
-                .setRequired(false)
-                .setStrategy(celix::dm::DependencyUpdateStrategy::locking)
-                .setCallbacks(&celix::rsa::RemoteServiceAdmin::addImportedServiceFactory, &celix::rsa::RemoteServiceAdmin::removeImportedServiceFactory);
-        cmp.createServiceDependency<celix::rsa::IExportServiceFactory>()
-                .setRequired(false)
-                .setStrategy(celix::dm::DependencyUpdateStrategy::locking)
-                .setCallbacks(&celix::rsa::RemoteServiceAdmin::addExportedServiceFactory, &celix::rsa::RemoteServiceAdmin::removeExportedServiceFactory);
-        cmp.build();
-
-        //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::SERVICE_EXPORTED_INTERFACES).append("=*)"))
-                .addAddWithPropertiesCallback([admin](const std::shared_ptr<void>& svc, const std::shared_ptr<const celix::Properties>& properties) {
-                    admin->addService(svc, properties);
-                })
-                .addRemWithPropertiesCallback([admin](const std::shared_ptr<void>& svc, const std::shared_ptr<const celix::Properties>& properties) {
-                    admin->removeService(svc, properties);
-                })
-                .build();
-    }
-private:
-    std::shared_ptr<celix::ServiceTracker<void>> _remoteServiceTracker{};
-};
-
-CELIX_GEN_CXX_BUNDLE_ACTIVATOR(AdminActivator)
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/bundles/cxx_remote_services/admin/include/RemoteServiceAdmin.h b/bundles/cxx_remote_services/admin/src/RemoteServiceAdmin.h
similarity index 63%
rename from bundles/cxx_remote_services/admin/include/RemoteServiceAdmin.h
rename to bundles/cxx_remote_services/admin/src/RemoteServiceAdmin.h
index 969a095..6101799 100644
--- a/bundles/cxx_remote_services/admin/include/RemoteServiceAdmin.h
+++ b/bundles/cxx_remote_services/admin/src/RemoteServiceAdmin.h
@@ -17,7 +17,6 @@
  * under the License.
  */
 
-
 #include <mutex>
 
 #include "celix/LogHelper.h"
@@ -35,6 +34,15 @@
 
 namespace celix::rsa {
 
+    /**
+     * @brief Remote Service Admin based on endpoint/proxy factories.
+     *
+     * A Remote Service Admin which does not contain on itself any technology, but relies on remote
+     * service endpoint/proxy factories to create exported/imported remote services.
+     *
+     * The RSA can be configured to use different remote service endpoint/proxy factories based on the
+     * intent of the remote service endpoint/proxy factories.
+     */
     class RemoteServiceAdmin {
     public:
         explicit RemoteServiceAdmin(celix::LogHelper logHelper);
@@ -57,24 +65,26 @@
     private:
         void createExportServices();
         void createImportServices();
+        bool isEndpointMatch(const celix::rsa::EndpointDescription& endpoint, const celix::rsa::IImportServiceFactory& factory) const;
+        bool isExportServiceMatch(const celix::Properties& svcProperties, const celix::rsa::IExportServiceFactory& factory) const;
 
-        celix::LogHelper _logHelper;
-        std::mutex _m{}; // protects below
+        celix::LogHelper logHelper;
+        std::mutex mutex{}; // protects below
 
 #if __cpp_lib_memory_resource
-        std::pmr::unsynchronized_pool_resource _memResource{};
+        std::pmr::unsynchronized_pool_resource memResource{};
 
-        std::pmr::unordered_map<std::string, std::shared_ptr<celix::rsa::IExportServiceFactory>> _exportServiceFactories{&_memResource}; //key = service name
-        std::pmr::unordered_map<std::string, std::shared_ptr<celix::rsa::IImportServiceFactory>> _importServiceFactories{&_memResource}; //key = service name
-        std::pmr::unordered_map<std::string, std::unique_ptr<celix::rsa::IImportServiceGuard>> _importedServices{&_memResource}; //key = endpoint id
-        std::pmr::unordered_map<long, std::unique_ptr<celix::rsa::IExportServiceGuard>> _exportedServices{&_memResource}; //key = service id
+        std::pmr::multimap<std::string, std::shared_ptr<celix::rsa::IExportServiceFactory>> exportServiceFactories{&memResource}; //key = service name
+        std::pmr::multimap<std::string, std::shared_ptr<celix::rsa::IImportServiceFactory>> importServiceFactories{&memResource}; //key = service name
+        std::pmr::unordered_map<std::string, std::unique_ptr<celix::rsa::IImportRegistration>> importedServices{&memResource}; //key = endpoint id
+        std::pmr::unordered_map<long, std::unique_ptr<celix::rsa::IExportRegistration>> exportedServices{&memResource}; //key = service id
 #else
-        std::unordered_map<std::string, std::shared_ptr<celix::rsa::IExportServiceFactory>> _exportServiceFactories{}; //key = service name
-        std::unordered_map<std::string, std::shared_ptr<celix::rsa::IImportServiceFactory>> _importServiceFactories{}; //key = service name
-        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
+        std::multimap<std::string, std::shared_ptr<celix::rsa::IExportServiceFactory>> exportServiceFactories{}; //key = service name
+        std::multimap<std::string, std::shared_ptr<celix::rsa::IImportServiceFactory>> importServiceFactories{}; //key = service name
+        std::unordered_map<std::string, std::unique_ptr<celix::rsa::IImportRegistration>> importedServices{}; //key = endpoint id
+        std::unordered_map<long, std::unique_ptr<celix::rsa::IExportRegistration>> exportedServices{}; //key = service id
 #endif
-        std::vector<std::shared_ptr<celix::rsa::EndpointDescription>> _toBeImportedServices{};
-        std::vector<std::shared_ptr<const celix::Properties>> _toBeExportedServices{};
+        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/RemoteServiceAdminActivator.cc b/bundles/cxx_remote_services/admin/src/RemoteServiceAdminActivator.cc
new file mode 100644
index 0000000..0b646ac
--- /dev/null
+++ b/bundles/cxx_remote_services/admin/src/RemoteServiceAdminActivator.cc
@@ -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.
+ */
+
+#include "celix/BundleActivator.h"
+#include "RemoteServiceAdmin.h"
+
+/**
+ * @Brief Remote Service Admin which use export/import service factories to import/export remote services.
+ *
+ * Supported intends and config are based on what is supported by the export/import service factories.
+ */
+class AdminActivator {
+public:
+    explicit AdminActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
+        auto admin = std::make_shared<celix::rsa::RemoteServiceAdmin>(celix::LogHelper{ctx, celix::typeName<celix::rsa::RemoteServiceAdmin>()});
+
+        auto& cmp = ctx->getDependencyManager()->createComponent(admin);
+        cmp.createServiceDependency<celix::rsa::EndpointDescription>()
+                .setRequired(false)
+                .setStrategy(celix::dm::DependencyUpdateStrategy::locking)
+                .setCallbacks(&celix::rsa::RemoteServiceAdmin::addEndpoint, &celix::rsa::RemoteServiceAdmin::removeEndpoint);
+        cmp.createServiceDependency<celix::rsa::IImportServiceFactory>()
+                .setRequired(false)
+                .setStrategy(celix::dm::DependencyUpdateStrategy::locking)
+                .setCallbacks(&celix::rsa::RemoteServiceAdmin::addImportedServiceFactory, &celix::rsa::RemoteServiceAdmin::removeImportedServiceFactory);
+        cmp.createServiceDependency<celix::rsa::IExportServiceFactory>()
+                .setRequired(false)
+                .setStrategy(celix::dm::DependencyUpdateStrategy::locking)
+                .setCallbacks(&celix::rsa::RemoteServiceAdmin::addExportedServiceFactory, &celix::rsa::RemoteServiceAdmin::removeExportedServiceFactory);
+        cmp.build();
+
+        //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::SERVICE_EXPORTED_INTERFACES).append("=*)"))
+                .addAddWithPropertiesCallback([admin](const std::shared_ptr<void>& svc, const std::shared_ptr<const celix::Properties>& properties) {
+                    admin->addService(svc, properties);
+                })
+                .addRemWithPropertiesCallback([admin](const std::shared_ptr<void>& svc, const std::shared_ptr<const celix::Properties>& properties) {
+                    admin->removeService(svc, properties);
+                })
+                .build();
+    }
+private:
+    std::shared_ptr<celix::ServiceTracker<void>> _remoteServiceTracker{};
+};
+
+CELIX_GEN_CXX_BUNDLE_ACTIVATOR(AdminActivator)
\ 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
index dcd900f..cd773be 100644
--- a/bundles/cxx_remote_services/discovery_configured/gtest/src/RsaConfiguredDiscoveryTestSuite.cc
+++ b/bundles/cxx_remote_services/discovery_configured/gtest/src/RsaConfiguredDiscoveryTestSuite.cc
@@ -54,7 +54,7 @@
     auto count = ctx->useServices<celix::rsa::EndpointDescription>()
             .addUseCallback([](auto& endpoint) {
                 EXPECT_NE(endpoint.getId(), "");
-                EXPECT_NE(endpoint.getConfigurationTypes(), "");
+                EXPECT_NE(endpoint.getConfigurationTypes(), std::vector<std::string>{});
                 EXPECT_NE(endpoint.getInterface(), "");
                 EXPECT_NE(endpoint.getFrameworkUUID(), "");
                 EXPECT_NE(endpoint.getProperties().get("endpoint.scope"), ""); //note async specific
diff --git a/bundles/cxx_remote_services/integration/src/CalculatorProvider.cc b/bundles/cxx_remote_services/integration/src/CalculatorProvider.cc
index dd4a3a2..ec4ea86 100644
--- a/bundles/cxx_remote_services/integration/src/CalculatorProvider.cc
+++ b/bundles/cxx_remote_services/integration/src/CalculatorProvider.cc
@@ -50,7 +50,8 @@
         cmp.createProvidedService<ICalculator>()
                 .addProperty("service.exported.interfaces", celix::typeName<ICalculator>())
                 .addProperty("endpoint.topic", "test")
-                .addProperty("endpoint.scope", "default");
+                .addProperty("endpoint.scope", "default")
+                .addProperty("service.exported.intents", "osgi.async");
         cmp.build();
     }
 };
diff --git a/bundles/cxx_remote_services/integration/src/TestExportImportRemoteServiceFactory.cc b/bundles/cxx_remote_services/integration/src/TestExportImportRemoteServiceFactory.cc
index 154e42b..d8a6962 100644
--- a/bundles/cxx_remote_services/integration/src/TestExportImportRemoteServiceFactory.cc
+++ b/bundles/cxx_remote_services/integration/src/TestExportImportRemoteServiceFactory.cc
@@ -158,10 +158,10 @@
 /**
  * A import service guard, which will remove the component if it goes out of scope.
  */
-class ComponentImportServiceGuard final : public celix::rsa::IImportServiceGuard {
+class ComponentImportRegistration final : public celix::rsa::IImportRegistration {
 public:
-    ComponentImportServiceGuard(std::shared_ptr<celix::BundleContext> _ctx, std::string _componentId) : ctx{std::move(_ctx)}, componentId{std::move(_componentId)} {}
-    ~ComponentImportServiceGuard() noexcept override {
+    ComponentImportRegistration(std::shared_ptr<celix::BundleContext> _ctx, std::string _componentId) : ctx{std::move(_ctx)}, componentId{std::move(_componentId)} {}
+    ~ComponentImportRegistration() noexcept override {
         auto context = ctx.lock();
         if (context) {
             context->getDependencyManager()->removeComponentAsync(componentId);
@@ -177,14 +177,12 @@
  */
 class CalculatorImportServiceFactory final : public celix::rsa::IImportServiceFactory {
 public:
+    static constexpr const char * const CONFIGS = "pubsub";
+
     explicit CalculatorImportServiceFactory(std::shared_ptr<celix::BundleContext> _ctx) : ctx{std::move(_ctx)}, logHelper{ctx, "celix::rsa::RemoteServiceFactory"} {}
+    ~CalculatorImportServiceFactory() noexcept override = default;
 
-    std::unique_ptr<celix::rsa::IImportServiceGuard> importService(const celix::rsa::EndpointDescription& endpoint) override {
-        if (endpoint.getConfigurationTypes() != "pubsub") {
-            ctx->logTrace("skipping endpoint, not pubsub configuration. Found config '%s'", endpoint.getConfigurationTypes().c_str());
-            return nullptr;
-        }
-
+    std::unique_ptr<celix::rsa::IImportRegistration> importService(const celix::rsa::EndpointDescription& endpoint) override {
         auto topic = endpoint.getProperties().get("endpoint.topic");
         auto scope = endpoint.getProperties().get("endpoint.topic");
         if (topic.empty() || scope.empty()) {
@@ -193,8 +191,17 @@
         }
 
         auto componentId = createImportedCalculatorComponent(endpoint);
-        return std::make_unique<ComponentImportServiceGuard>(ctx, std::move(componentId));
+        return std::make_unique<ComponentImportRegistration>(ctx, std::move(componentId));
     }
+
+    [[nodiscard]] const std::string& getRemoteServiceType() const override {
+        return serviceType;
+    }
+
+    const std::vector<std::string>& getSupportedConfigs() const override {
+        return configs;
+    }
+
 private:
     std::string createImportedCalculatorComponent(const celix::rsa::EndpointDescription& endpoint) {
         auto invokeTopic = endpoint.getProperties().get("endpoint.topic") + "_invoke";
@@ -243,6 +250,8 @@
         return cmp.getUUID();
     }
 
+    const std::string serviceType = celix::typeName<ICalculator>();
+    const std::vector<std::string> configs = celix::split(CONFIGS);
     std::shared_ptr<celix::BundleContext> ctx;
     celix::LogHelper logHelper;
     std::mutex mutex{}; //protects below
@@ -341,10 +350,10 @@
 /**
  * A import service guard, which will remove the component if it goes out of scope.
  */
-class ComponentExportServiceGuard final : public celix::rsa::IExportServiceGuard {
+class ComponentExportRegistration final : public celix::rsa::IExportRegistration {
 public:
-    ComponentExportServiceGuard(std::shared_ptr<celix::BundleContext> _ctx, std::string _componentId) : ctx{std::move(_ctx)}, componentId{std::move(_componentId)} {}
-    ~ComponentExportServiceGuard() noexcept override {
+    ComponentExportRegistration(std::shared_ptr<celix::BundleContext> _ctx, std::string _componentId) : ctx{std::move(_ctx)}, componentId{std::move(_componentId)} {}
+    ~ComponentExportRegistration() noexcept override {
         auto context = ctx.lock();
         if (context) {
             context->getDependencyManager()->removeComponentAsync(componentId);
@@ -360,10 +369,14 @@
  */
 class CalculatorExportServiceFactory final : public celix::rsa::IExportServiceFactory {
 public:
+    static constexpr const char * const CONFIGS = "pubsub";
+    static constexpr const char * const INTENTS = "osgi.async";
+
     explicit CalculatorExportServiceFactory(std::shared_ptr<celix::BundleContext> _ctx) : ctx{std::move(_ctx)},
                                                                                           logHelper{ctx, "celix::rsa::RemoteServiceFactory"} {}
+    ~CalculatorExportServiceFactory() noexcept override = default;
 
-    std::unique_ptr<celix::rsa::IExportServiceGuard> exportService(const celix::Properties& serviceProperties) override {
+    std::unique_ptr<celix::rsa::IExportRegistration> exportService(const celix::Properties& serviceProperties) override {
         auto topic = serviceProperties.get("endpoint.topic");
         auto scope = serviceProperties.get("endpoint.topic");
         if (topic.empty() || scope.empty()) {
@@ -372,8 +385,21 @@
         }
 
         auto componentId = createExportedCalculatorComponent(serviceProperties);
-        return std::make_unique<ComponentExportServiceGuard>(ctx, std::move(componentId));
+        return std::make_unique<ComponentExportRegistration>(ctx, std::move(componentId));
     }
+
+    [[nodiscard]] const std::string& getRemoteServiceType() const override {
+        return serviceType;
+    }
+
+    [[nodiscard]] const std::vector<std::string>& getSupportedIntents() const override {
+        return intents;
+    }
+
+    [[nodiscard]] const std::vector<std::string>& getSupportedConfigs() const override {
+        return configs;
+    }
+
 private:
     std::string createExportedCalculatorComponent(const celix::Properties& serviceProperties) {
         auto invokeTopic = serviceProperties.get("endpoint.topic") + "_invoke";
@@ -417,6 +443,9 @@
         return cmp.getUUID();
     }
 
+    const std::string serviceType = celix::typeName<ICalculator>();
+    const std::vector<std::string> configs = celix::split(CONFIGS);
+    const std::vector<std::string> intents = celix::split(INTENTS);
     std::shared_ptr<celix::BundleContext> ctx;
     celix::LogHelper logHelper;
     std::mutex mutex{}; //protects below
@@ -428,12 +457,15 @@
         ctx->logInfo("Starting TestExportImportRemoteServiceFactory");
         registrations.emplace_back(
                 ctx->registerService<celix::rsa::IImportServiceFactory>(std::make_shared<CalculatorImportServiceFactory>(ctx))
-                        .addProperty(celix::rsa::IImportServiceFactory::TARGET_SERVICE_NAME, celix::typeName<ICalculator>())
+                        .addProperty(celix::rsa::IImportServiceFactory::REMOTE_SERVICE_TYPE, celix::typeName<ICalculator>())
+                        .addProperty(celix::rsa::REMOTE_CONFIGS_SUPPORTED, CalculatorImportServiceFactory::CONFIGS)
                         .build()
         );
         registrations.emplace_back(
                 ctx->registerService<celix::rsa::IExportServiceFactory>(std::make_shared<CalculatorExportServiceFactory>(ctx))
-                        .addProperty(celix::rsa::IExportServiceFactory::TARGET_SERVICE_NAME, celix::typeName<ICalculator>())
+                        .addProperty(celix::rsa::IExportServiceFactory::REMOTE_SERVICE_TYPE, celix::typeName<ICalculator>())
+                        .addProperty(celix::rsa::REMOTE_CONFIGS_SUPPORTED, CalculatorExportServiceFactory::CONFIGS)
+                        .addProperty(celix::rsa::REMOTE_INTENTS_SUPPORTED, CalculatorExportServiceFactory::INTENTS)
                         .build()
         );
         registrations.emplace_back(
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
index 4b43142..a747deb 100644
--- a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/EndpointDescription.h
+++ b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/EndpointDescription.h
@@ -21,6 +21,7 @@
 #include <string>
 #include <map>
 
+#include "celix/Utils.h"
 #include "celix/Constants.h"
 #include "celix/Properties.h"
 #include "celix/rsa/RemoteConstants.h"
@@ -51,7 +52,13 @@
          *
          * @param The properties from which to create the Endpoint Description.
          */
-        explicit EndpointDescription(celix::Properties properties) : endpointProperties{std::move(properties)} {
+        explicit EndpointDescription(celix::Properties properties) :
+                endpointProperties{std::move(properties)},
+                endpointId{endpointProperties.get(celix::rsa::ENDPOINT_ID)},
+                configurationTypes{celix::split(endpointProperties.get(celix::rsa::SERVICE_IMPORTED_CONFIGS))},
+                frameworkUUID{endpointProperties.get(celix::rsa::ENDPOINT_FRAMEWORK_UUID)},
+                intents{celix::split(endpointProperties.get(celix::rsa::SERVICE_INTENTS))},
+                serviceName{endpointProperties.get(celix::SERVICE_NAME)} {
             checkValidEndpoint();
         }
 
@@ -66,8 +73,13 @@
          * @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)} {
+        EndpointDescription(std::string_view frameworkUUID, const celix::Properties& serviceProperties, const celix::Properties& rsaProperties = {}) :
+                endpointProperties{importedProperties(frameworkUUID, serviceProperties, rsaProperties)},
+                endpointId{endpointProperties.get(celix::rsa::ENDPOINT_ID)},
+                configurationTypes{celix::split(endpointProperties.get(celix::rsa::SERVICE_IMPORTED_CONFIGS))},
+                frameworkUUID{endpointProperties.get(celix::rsa::ENDPOINT_FRAMEWORK_UUID)},
+                intents{celix::split(endpointProperties.get(celix::rsa::SERVICE_INTENTS))},
+                serviceName{endpointProperties.get(celix::SERVICE_NAME)} {
             checkValidEndpoint();
         }
 
@@ -79,8 +91,8 @@
          * 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);
+        [[nodiscard]] const std::string& getId() const {
+            return endpointId;
         }
 
         /**
@@ -93,8 +105,8 @@
          * 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);
+        [[nodiscard]] const std::vector<std::string>& getConfigurationTypes() const {
+            return configurationTypes;
         }
 
         /**
@@ -102,8 +114,8 @@
          *
          * 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);
+        [[nodiscard]] const std::string& getFrameworkUUID() const {
+            return frameworkUUID;
         }
 
         /**
@@ -114,17 +126,16 @@
          * 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);
+        [[nodiscard]] const std::vector<std::string>& getIntents() const {
+            return 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);
+        [[nodiscard]] const std::string& getInterface() const {
+            return serviceName;
         }
 
         /**
@@ -170,17 +181,22 @@
 
         void checkValidEndpoint() const {
             std::string baseMsg = "Invalid properties for EndpointDescription, missing mandatory property ";
-            if (endpointProperties.get(celix::rsa::ENDPOINT_ID).empty()) {
+            if (endpointId.empty()) {
                 throw celix::rsa::RemoteServicesException{baseMsg.append(celix::rsa::ENDPOINT_ID)};
             }
-            if (endpointProperties.get(celix::rsa::ENDPOINT_ID).empty()) {
+            if (configurationTypes.empty()) {
                 throw celix::rsa::RemoteServicesException{baseMsg.append(celix::rsa::SERVICE_IMPORTED_CONFIGS)};
             }
-            if (endpointProperties.get(celix::SERVICE_NAME).empty()) {
+            if (serviceName.empty()) {
                 throw celix::rsa::RemoteServicesException{baseMsg.append(celix::SERVICE_NAME)};
             }
         }
-        
+
         const celix::Properties endpointProperties;
+        const std::string endpointId;
+        const std::vector<std::string> configurationTypes;
+        const std::string frameworkUUID;
+        const std::vector<std::string> intents;
+        const std::string serviceName;
     };
 } // end namespace celix::rsa.
diff --git a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IExportRegistration.h b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IExportRegistration.h
new file mode 100644
index 0000000..81944c6
--- /dev/null
+++ b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IExportRegistration.h
@@ -0,0 +1,31 @@
+/*
+ * 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
+
+namespace celix::rsa {
+
+    /**
+     * @brief IExportRegistration class which represent a (opaque) exported service.
+     * If lifetime of this object expires it should remove the underlining exported service.
+     * */
+    class IExportRegistration {
+    public:
+        virtual ~IExportRegistration() noexcept = default;
+    };
+}
\ No newline at end of file
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 66383b6..73ebe28 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,24 +19,16 @@
 #pragma once
 
 #include <memory>
-#include "celix/rsa/EndpointDescription.h"
+
+#include "celix/rsa/IExportRegistration.h"
 
 namespace celix::rsa {
 
     /**
-     * @brief IExportServiceGuard class which represent a (opaque) exported service.
-     * If lifetime of this object expires it should remove the underlining exported service.
-     * */
-    class IExportServiceGuard {
-    public:
-        virtual ~IExportServiceGuard() noexcept = default;
-    };
-
-    /**
      * @brief A export service factory for a specific service type.
      *
-     * The service type which this export service factory targets is provided with
-     * the mandatory celix::rsa::IExportServiceFactory::TARGET_SERVICE_NAME service property.
+     * the mandatory service properties:
+     *  - celix::rsa::IExportServiceFactory::REMOTE_SERVICE_TYPE
      *
      */
     class IExportServiceFactory {
@@ -44,16 +36,34 @@
         /**
          * @brief The service name for which this factory can created exported services.
          */
-        static constexpr const char * const TARGET_SERVICE_NAME = "target.service.name";
+        static constexpr const char * const REMOTE_SERVICE_TYPE = "remote.service.type";
 
         virtual ~IExportServiceFactory() noexcept = default;
 
         /**
-         * @brief Exports the service identified with svcId
-         * @param svcId The service id of the exported service.
-         * @return A ExportService.
+         * @brief The service name for which this factory can export services as remote services.
+         *
+         * The value should be based on the "remote.service.type" service property of
+         * this import service factory.
+         */
+        [[nodiscard]] virtual const std::string& getRemoteServiceType() const = 0;
+
+        /**
+         * @Brief The supported intents for this export service factory.
+         */
+        [[nodiscard]] virtual const std::vector<std::string>& getSupportedIntents() const = 0;
+
+        /**
+         * @Brief The supported configs for this export service factory.
+         */
+        [[nodiscard]] virtual const std::vector<std::string>& getSupportedConfigs() const = 0;
+
+        /**
+         * @brief Exports the service associated with the provided serviceProperties.
+         * @param serviceProperties The service properties of the to be exported service.
+         * @return A new export registration.
          * @throws celix::rsa::RemoteServicesException if the export failed.
          */
-        virtual std::unique_ptr<celix::rsa::IExportServiceGuard> exportService(const celix::Properties& serviceProperties) = 0;
+        [[nodiscard]] virtual std::unique_ptr<celix::rsa::IExportRegistration> exportService(const celix::Properties& serviceProperties) = 0;
     };
 }
\ No newline at end of file
diff --git a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IImportRegistration.h b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IImportRegistration.h
new file mode 100644
index 0000000..cb366dc
--- /dev/null
+++ b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/IImportRegistration.h
@@ -0,0 +1,31 @@
+/*
+ * 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
+
+namespace celix::rsa {
+
+    /**
+     * @brief IImportRegistration class which represent a (opaque) imported service.
+     * If lifetime of this object expires it should remove the underlining imported service.
+     */
+    class IImportRegistration {
+    public:
+        virtual ~IImportRegistration() noexcept = default;
+    };
+}
\ No newline at end of file
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 7c6f3a1..dd9d3c6 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,41 +19,47 @@
 #pragma once
 
 #include <memory>
+
 #include "celix/rsa/EndpointDescription.h"
+#include "celix/rsa/IImportRegistration.h"
 
 namespace celix::rsa {
 
     /**
-     * @brief IImportServiceGuard class which represent a (opaque) imported service.
-     * If lifetime of this object expires it should remove the underlining imported service.
-     */
-    class IImportServiceGuard {
-    public:
-        virtual ~IImportServiceGuard() noexcept = default;
-    };
-
-    /**
      * @brief A import service factory for a specific service type.
      *
-     * The service type which this import service factory targets is provided with
-     * the mandatory celix::rsa::IImportServiceFactory::TARGET_SERVICE_NAME service property.
+     * The service type which this export service factory targets is provided with `REMOTE_SERVICE_TYPE` and
+     * the supported configs with `REMOTE_CONFIGS_SUPPORTED`.
      *
      */
     class IImportServiceFactory {
     public:
         /**
-         * @brief The service name for which this factory can created exported services.
+         * @brief The service name for which this factory can import remote services.
          */
-        static constexpr const char * const TARGET_SERVICE_NAME = "target.service.name";
+        static constexpr const char * const REMOTE_SERVICE_TYPE = "remote.service.type";
 
         virtual ~IImportServiceFactory() noexcept = default;
 
         /**
-         * @brief Imports the service identified with svcId
-         * @param svcId The service id of the exported service.
-         * @return A ImportService.
+         * @brief The service name for which this factory can import remote services.
+         *
+         * The value should be based on the "remote.service.type" service property of
+         * this import service factory.
+         */
+        [[nodiscard]] virtual const std::string& getRemoteServiceType() const = 0;
+
+        /**
+         * @Brief The supported configs for this import service factory.
+         */
+        [[nodiscard]] virtual const std::vector<std::string>& getSupportedConfigs() const = 0;
+
+        /**
+         * @brief Imports a service for the provided remote service endpoint description.
+         * @param endpoint The endpoint description describing the remote service.
+         * @return A new import registration.
          * @throws celix::rsa::RemoteServicesException if the import failed.
          */
-        virtual std::unique_ptr<celix::rsa::IImportServiceGuard> importService(const celix::rsa::EndpointDescription& endpoint) = 0;
+        [[nodiscard]] virtual std::unique_ptr<celix::rsa::IImportRegistration> 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
index 9dae5e3..7cae240 100644
--- a/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/RemoteConstants.h
+++ b/bundles/cxx_remote_services/rsa_spi/include/celix/rsa/RemoteConstants.h
@@ -91,7 +91,7 @@
     /**
      * @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
+     * Note for Celix only 1 interface can be register per service registration, 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.
diff --git a/bundles/logging/log_helper/include/celix/LogHelper.h b/bundles/logging/log_helper/include/celix/LogHelper.h
index 3f447f2..c22cb35 100644
--- a/bundles/logging/log_helper/include/celix/LogHelper.h
+++ b/bundles/logging/log_helper/include/celix/LogHelper.h
@@ -32,7 +32,7 @@
         /**
          * @brief Logs to celix_logHelper_log using the CELIX_LOG_LEVEL_TRACE level, printf style
          */
-        void trace(const char *format, ...) {
+        void trace(const char *format, ...) const {
             va_list args;
             va_start(args, format);
             vlog(CELIX_LOG_LEVEL_TRACE, format, args);
@@ -42,7 +42,7 @@
         /**
          * @brief Logs to celix_logHelper_log using the CELIX_LOG_LEVEL_DEBUG level, printf style
          */
-        void debug(const char *format, ...) {
+        void debug(const char *format, ...) const {
             va_list args;
             va_start(args, format);
             vlog(CELIX_LOG_LEVEL_DEBUG, format, args);
@@ -52,7 +52,7 @@
         /**
          * @brief Logs to celix_logHelper_log using the CELIX_LOG_LEVEL_INFO level, printf style
          */
-        void info(const char *format, ...) {
+        void info(const char *format, ...) const {
             va_list args;
             va_start(args, format);
             vlog(CELIX_LOG_LEVEL_INFO, format, args);
@@ -62,7 +62,7 @@
         /**
          * @brief Logs to celix_logHelper_log using the CELIX_LOG_LEVEL_WARNING level, printf style
          */
-        void warning(const char *format, ...) {
+        void warning(const char *format, ...) const {
             va_list args;
             va_start(args, format);
             vlog(CELIX_LOG_LEVEL_WARNING, format, args);
@@ -72,7 +72,7 @@
         /**
          * @brief Logs to celix_logHelper_log using the CELIX_LOG_LEVEL_ERROR level, printf style
          */
-        void error(const char *format, ...) {
+        void error(const char *format, ...) const {
             va_list args;
             va_start(args, format);
             vlog(CELIX_LOG_LEVEL_ERROR, format, args);
@@ -82,7 +82,7 @@
         /**
          * @brief Logs to celix_logHelper_log using the CELIX_LOG_LEVEL_FATAL level, printf style
          */
-        void fatal(const char *format, ...) {
+        void fatal(const char *format, ...) const {
             va_list args;
             va_start(args, format);
             vlog(CELIX_LOG_LEVEL_FATAL, format, args);
@@ -94,7 +94,7 @@
          *
          * Silently ignores log level CELIX_LOG_LEVEL_DISABLED.
          */
-        void vlog(celix_log_level_e level, const char *format, va_list formatArgs) {
+        void vlog(celix_log_level_e level, const char *format, va_list formatArgs) const {
             celix_logHelper_vlog(logHelper.get(), level, format, formatArgs);
         }