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/10/10 16:48:51 UTC

[celix] 01/01: Updates C++ header only to C++17, except the for already existing C++ Dep Man headers

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

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

commit a620a2e2864e6414668ffbc2b06fa09097eae65b
Author: Pepijn Noltes <pn...@apache.org>
AuthorDate: Sun Oct 10 18:48:33 2021 +0200

    Updates C++ header only to C++17, except the for already existing C++ Dep Man headers
---
 CMakeLists.txt                                     |   2 +-
 README.md                                          |   2 +-
 bundles/CMakeLists.txt                             |   7 +-
 bundles/shell/shell/CMakeLists.txt                 |   1 -
 bundles/shell/shell/src/lb_command.c               |  18 +-
 cmake/cmake_celix/BundlePackaging.cmake            |  19 +-
 cmake/cmake_celix/ContainerPackaging.cmake         |   2 +-
 documents/cmake_commands/README.md                 |  21 +-
 examples/CMakeLists.txt                            |   2 +-
 libs/framework/CMakeLists.txt                      |   6 +-
 libs/framework/gtest/CMakeLists.txt                |  38 ++-
 .../gtest/src/CxxBundleContextTestSuite.cc         |  75 +++++-
 .../gtest/src/CxxFrameworkFactoryTestSuite.cc      |   1 +
 libs/framework/gtest/src/CxxUtilsTestSuite.cc      |  62 -----
 .../gtest/src/DependencyManagerTestSuite.cc        |  46 +++-
 libs/framework/gtest/subdir/CMakeLists.txt         |   3 +-
 .../framework/gtest/subdir/src/{foo.c => sublib.c} |   6 +-
 libs/framework/include/bundle.h                    |   3 +
 libs/framework/include/celix/Bundle.h              |  75 +++++-
 libs/framework/include/celix/BundleActivator.h     | 112 ++++-----
 libs/framework/include/celix/BundleContext.h       |  20 +-
 libs/framework/include/celix/Constants.h           |   8 +
 libs/framework/include/celix/Exception.h           |   9 +-
 libs/framework/include/celix/Framework.h           |   6 +-
 libs/framework/include/celix/FrameworkFactory.h    |   2 +-
 .../include/celix/ServiceRegistrationBuilder.h     |  12 +-
 libs/framework/include/celix/TrackerBuilders.h     |  39 +--
 libs/framework/include/celix/Trackers.h            |  21 +-
 libs/framework/include/celix/UseServiceBuilder.h   |   6 +-
 libs/framework/include/celix/Utils.h               | 122 ---------
 libs/framework/include/celix/dm/Component_Impl.h   |  12 +-
 libs/framework/include/celix_bundle.h              |  24 +-
 libs/framework/include/celix_bundle_activator.h    |  42 +++-
 libs/framework/include/celix_bundle_context.h      | 280 ++++++++++++---------
 libs/framework/include/celix_constants.h           |  12 +
 libs/framework/include/module.h                    |   4 +
 libs/framework/src/bundle.c                        |  31 ++-
 libs/framework/src/bundle_context.c                |   1 +
 libs/framework/src/bundle_private.h                |   5 +-
 .../{celix_library_loader.c => celix_libloader.c}  |  25 +-
 .../{celix_library_loader.h => celix_libloader.h}  |  34 ++-
 libs/framework/src/framework.c                     |   5 +-
 libs/framework/src/manifest_parser.c               |  36 +--
 libs/framework/src/manifest_parser.h               |   2 +
 libs/framework/src/module.c                        |  62 +++--
 libs/framework/src/service_registry.c              |   5 +-
 libs/framework/src/service_tracker.c               |  22 +-
 libs/framework/src/service_tracker_private.h       |   2 +
 libs/promises/api/celix/impl/SharedPromiseState.h  |   4 +-
 libs/utils/gtest/CMakeLists.txt                    |   3 +
 .../gtest/src/CxxFilterTestSuite.cc                |   4 +-
 .../gtest/src/CxxPropertiesTestSuite.cc            |  11 +-
 libs/utils/gtest/src/CxxUtilsTestSuite.cc          | 121 +++++++++
 libs/{framework => utils}/include/celix/Filter.h   |  36 ++-
 .../include/celix/Properties.h                     |  34 ++-
 libs/utils/include/celix/Utils.h                   | 240 ++++++++++++++++++
 56 files changed, 1203 insertions(+), 600 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 88ec2f6..deea4e5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -48,7 +48,7 @@ set(CMAKE_C_FLAGS "-D_GNU_SOURCE -std=gnu99 -fPIC ${CMAKE_C_FLAGS}")
 set(CMAKE_C_FLAGS "-Wall -Werror ${CMAKE_C_FLAGS}")
 
 # Set C++ specific flags
-set(CMAKE_CXX_STANDARD 17) #test code and newer C++ bundles can be C++17. The Celix::framework and Celix::CxxShell is still C++11
+set(CMAKE_CXX_STANDARD 17) #Celix header only C++ supported is based on C++17.
 set(CMAKE_CXX_FLAGS "-fno-rtti ${CMAKE_CXX_FLAGS}")
 set(CMAKE_CXX_FLAGS "-Wall -Werror -Wextra -Weffc++ ${CMAKE_CXX_FLAGS}")
 
diff --git a/README.md b/README.md
index 2480116..546e4c2 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ limitations under the License.
 [![codecov](https://codecov.io/gh/apache/celix/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/celix)
 [![Coverity Scan Build Status](https://scan.coverity.com/projects/6685/badge.svg)](https://scan.coverity.com/projects/6685)
 
-Apache Celix is an implementation of the OSGi specification adapted to C and C++ (C++11). It is a framework to develop (dynamic) modular software applications using component and/or service-oriented programming.
+Apache Celix is an implementation of the OSGi specification adapted to C and C++ (C++17). It is a framework to develop (dynamic) modular software applications using component and/or service-oriented programming.
 
 ## Documentation
 - [Building Apache Celix](documents/building/README.md)
diff --git a/bundles/CMakeLists.txt b/bundles/CMakeLists.txt
index fad202b..d84f4e0 100644
--- a/bundles/CMakeLists.txt
+++ b/bundles/CMakeLists.txt
@@ -15,6 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
+# Celix C bundles
 add_subdirectory(http_admin)
 add_subdirectory(logging)
 add_subdirectory(shell)
@@ -22,4 +23,8 @@ add_subdirectory(device_access)
 add_subdirectory(deployment_admin)
 add_subdirectory(remote_services)
 add_subdirectory(pubsub)
-add_subdirectory(cxx_remote_services)
+
+# Celix C++17 bundles
+if (CELIX_CXX)
+    add_subdirectory(cxx_remote_services)
+endif ()
diff --git a/bundles/shell/shell/CMakeLists.txt b/bundles/shell/shell/CMakeLists.txt
index 432a52c..0d8ab19 100644
--- a/bundles/shell/shell/CMakeLists.txt
+++ b/bundles/shell/shell/CMakeLists.txt
@@ -70,7 +70,6 @@ if (SHELL)
 	add_library(Celix::shell ALIAS shell)
 
 	if (CELIX_CXX)
-		set(CMAKE_CXX_STANDARD 11) #Note that the Celix::CxxShell uses C++11 only
 		add_celix_bundle(ShellCxx
 			SYMBOLIC_NAME "apache::celix::CxxShell"
 			VERSION "2.1.0"
diff --git a/bundles/shell/shell/src/lb_command.c b/bundles/shell/shell/src/lb_command.c
index 902b67e..73af76d 100644
--- a/bundles/shell/shell/src/lb_command.c
+++ b/bundles/shell/shell/src/lb_command.c
@@ -135,23 +135,7 @@ static void lbCommand_listBundles(celix_bundle_context_t *ctx, const lb_options_
         }
 
         if (sub_status == CELIX_SUCCESS) {
-            if (id == CELIX_FRAMEWORK_BUNDLE_ID) {
-                name_str = "Celix framework";
-            } else {
-                bundle_archive_t* arch = NULL;
-                bundle_revision_t* rev = NULL;
-                manifest_pt man = NULL;
-                bundle_getArchive(bundle_ptr, &arch);
-                if (arch != NULL) {
-                    bundleArchive_getCurrentRevision(arch, &rev);
-                    if (rev != NULL) {
-                        bundleRevision_getManifest(rev, &man);
-                        if (man != NULL) {
-                            name_str = manifest_getValue(man, "Bundle-Name");
-                        }
-                    }
-                }
-            }
+            name_str = celix_bundle_getName(bundle_ptr);
         }
 
         if (sub_status == CELIX_SUCCESS) {
diff --git a/cmake/cmake_celix/BundlePackaging.cmake b/cmake/cmake_celix/BundlePackaging.cmake
index 57004c8..844625a 100644
--- a/cmake/cmake_celix/BundlePackaging.cmake
+++ b/cmake/cmake_celix/BundlePackaging.cmake
@@ -381,10 +381,27 @@ function(bundle_private_libs)
 endfunction()
 
 #[[
-Add libraries to a bundle. The libraries should be cmake library targets or an absolute path to an existing library.
+Add libraries to a bundle. A libraries should be a cmake library target or an absolute path to existing library.
 
 The libraries will be copied into the bundle zip and activator library will be linked (PRIVATE) against them.
 
+Apache Celix uses dlopen with RTLD_LOCAL to load the activator library in a bundle.
+It is important to note that dlopen will always load the activator library,
+but not always load the libraries the bundle activator library is linked against.
+If the activator library is linked against a library which is already loaded, the already loaded library will be used.
+More specifically dlopen will decide this based on the NEEDED header in the activator library
+and the SO_NAME headers of the already loaded libraries.
+
+For example installing in order:
+ - Bundle A with a private library libfoo (SONAME=libfoo.so) and
+ - Bundle B with a private library libfoo (SONAME=libfoo.so).
+Will result in Bundle B also using libfoo loaded from the cache dir in Bundle A.
+
+This also applies if multiple Celix frameworks are created in the same process. For example installed in order:
+ - Bundle A with a private library libfoo (SONAME=libfoo.so) in Celix Framework A and
+ - The same Bundle A in Celix Framework B.
+Will result in Bundle A from Framework B to use the libfoo loaded from the cache dir of Bundle A in framework A.
+
 celix_bundle_private_libs(<bundle_target>
     lib1 lib2 ...
 )
diff --git a/cmake/cmake_celix/ContainerPackaging.cmake b/cmake/cmake_celix/ContainerPackaging.cmake
index 60c2bd4..83a98c8 100644
--- a/cmake/cmake_celix/ContainerPackaging.cmake
+++ b/cmake/cmake_celix/ContainerPackaging.cmake
@@ -202,7 +202,7 @@ $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_2>>:CELIX
 $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_3>>:CELIX_AUTO_START_3=$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_3>, >\\n>\\
 $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_4>>:CELIX_AUTO_START_4=$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_4>, >\\n>\\
 $<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_5>>:CELIX_AUTO_START_5=$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_5>, >\\n>\\
-$<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_6>>:CELIX_AUTO_START_6=$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_6>, >\\n>\\
+$<$<BOOL:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_6>>:CELIX_AUTO_START_5=$<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_BUNDLES_LEVEL_6>, >\\n>\\
 $<JOIN:$<TARGET_PROPERTY:${CONTAINER_TARGET},CONTAINER_EMBEDDED_PROPERTIES>,\\n\\
 >\";
 
diff --git a/documents/cmake_commands/README.md b/documents/cmake_commands/README.md
index 6f9a398..d6f79dd 100644
--- a/documents/cmake_commands/README.md
+++ b/documents/cmake_commands/README.md
@@ -87,7 +87,26 @@ add_celix_bundle(<bundle_target_name>
 ## celix_bundle_private_libs
 Add libraries to a bundle. The libraries should be cmake library targets or an absolute path to an existing library.
 
-The libraries will be copied into the bundle zip and activator library will be linked (PRIVATE) against them. 
+The libraries will be copied into the bundle zip and activator library will be linked (PRIVATE) against them.
+
+Apache Celix uses dlopen with RTLD_LOCAL to load the activator library in a bundle.
+It is important to note that dlopen will always load the activator library,
+but not always load the libraries the bundle activator library is linked against.
+If the activator library is linked against a library which is already loaded, the already loaded library will be used.
+More specifically dlopen will decide this based on the NEEDED header in the activator library
+and the SO_NAME headers of the already loaded libraries.
+
+For example installing in order:
+- Bundle A with a private library libfoo (SONAME=libfoo.so) and
+- Bundle B with a private library libfoo (SONAME=libfoo.so).
+  Will result in Bundle B also using libfoo loaded from the cache dir in Bundle A.
+
+This also applies if multiple Celix frameworks are created in the same process. For example installed in order:
+- Bundle A with a private library libfoo (SONAME=libfoo.so) in Celix Framework A and
+- The same Bundle A in Celix Framework B.
+  Will result in Bundle A from Framework B to use the libfoo loaded from the cache dir of Bundle A in framework A.
+ 
+Will result in BundleA from framework B to use the libfoo loaded in BundleA from framework A.
 
 ```CMake
 celix_bundle_private_libs(<bundle_target>
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 80b88e7..316720b 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -30,6 +30,6 @@
 cmake_minimum_required (VERSION 3.4)
 project (CelixUse C CXX)
 set(CMAKE_C_FLAGS "-D_GNU_SOURCE -std=gnu99 ${CMAKE_C_FLAGS}")
-set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD 17)
 find_package(Celix REQUIRED)
 add_subdirectory(celix-examples examples)
diff --git a/libs/framework/CMakeLists.txt b/libs/framework/CMakeLists.txt
index cfc1d22..f0bcb10 100644
--- a/libs/framework/CMakeLists.txt
+++ b/libs/framework/CMakeLists.txt
@@ -19,8 +19,6 @@ find_package(ZLIB REQUIRED)
 find_package(UUID REQUIRED)
 find_package(CURL REQUIRED)
 
-set(CMAKE_CXX_STANDARD 11) #Note that the Celix framework C++ (header only) library uses C++11 only
-
 set(SOURCES
         src/attribute.c src/bundle.c src/bundle_archive.c src/bundle_cache.c
         src/bundle_context.c src/bundle_revision.c src/capability.c src/celix_errorcodes.c
@@ -32,10 +30,10 @@ set(SOURCES
         src/celix_log.c src/celix_launcher.c
         src/celix_framework_factory.c
         src/dm_dependency_manager_impl.c src/dm_component_impl.c
-        src/dm_service_dependency.c src/celix_library_loader.c
+        src/dm_service_dependency.c src/celix_libloader.c
         src/framework_bundle_lifecycle_handler.c
         src/celix_bundle_state.c
-)
+        )
 add_library(framework SHARED ${SOURCES})
 set_target_properties(framework PROPERTIES OUTPUT_NAME "celix_framework")
 target_include_directories(framework PUBLIC
diff --git a/libs/framework/gtest/CMakeLists.txt b/libs/framework/gtest/CMakeLists.txt
index 9880463..1f6d2e9 100644
--- a/libs/framework/gtest/CMakeLists.txt
+++ b/libs/framework/gtest/CMakeLists.txt
@@ -16,6 +16,10 @@
 # under the License.
 
 add_celix_bundle(simple_test_bundle1 NO_ACTIVATOR VERSION 1.0.0)
+celix_bundle_name(simple_test_bundle1 "Simple Test Bundle")
+celix_bundle_group(simple_test_bundle1 "test/group")
+celix_bundle_description(simple_test_bundle1 "Test Description")
+
 add_celix_bundle(simple_test_bundle2 NO_ACTIVATOR VERSION 1.0.0)
 add_celix_bundle(simple_test_bundle3 NO_ACTIVATOR VERSION 1.0.0)
 add_celix_bundle(bundle_with_exception SOURCES src/nop_activator.c VERSION 1.0.0)
@@ -34,22 +38,20 @@ endif()
 add_dependencies(unresolveable_bundle sublib)
 
 add_executable(test_framework
-    src/single_framework_test.cpp
-    src/multiple_frameworks_test.cpp
-    src/bundle_context_bundles_tests.cpp
-    src/bundle_context_services_test.cpp
-    src/DependencyManagerTestSuite.cc
+        src/single_framework_test.cpp
+        src/multiple_frameworks_test.cpp
+        src/bundle_context_bundles_tests.cpp
+        src/bundle_context_services_test.cpp
+        src/DependencyManagerTestSuite.cc
         src/CxxBundleContextTestSuite.cc
-        src/CxxPropertiesTestSuite.cc
         src/HelloWorldCxxActivator.cc
-        src/CxxFilterTestSuite.cc
         src/CxxFrameworkFactoryTestSuite.cc
-        src/CxxBundleActivatorTestSuite.cc
-        src/CxxUtilsTestSuite.cc
-)
-
-target_link_libraries(test_framework Celix::framework CURL::libcurl GTest::gtest GTest::gtest_main)
-add_dependencies(test_framework simple_test_bundle1_bundle simple_test_bundle2_bundle simple_test_bundle3_bundle simple_test_bundle4_bundle simple_test_bundle5_bundle bundle_with_exception_bundle unresolveable_bundle_bundle simple_cxx_bundle)
+        src/CxxBundleActivatorTestSuite.cc)
+target_link_libraries(test_framework PRIVATE Celix::framework CURL::libcurl GTest::gtest GTest::gtest_main)
+add_celix_bundle_dependencies(test_framework
+        simple_test_bundle1
+        simple_test_bundle2 simple_test_bundle3 simple_test_bundle4
+        simple_test_bundle5 bundle_with_exception unresolveable_bundle simple_cxx)
 target_include_directories(test_framework PRIVATE ../src)
 
 celix_get_bundle_file(simple_cxx_bundle SIMPLE_CXX_BUNDLE_LOC)
@@ -73,3 +75,13 @@ configure_file(framework2.properties.in framework2.properties @ONLY)
 add_test(NAME test_framework COMMAND test_framework)
 setup_target_for_coverage(test_framework SCAN_DIR ..)
 
+#Setting standard to C++11 and testing C++ dependency manager to ensure that this still support C++11.
+#This ensure that the C++11 dependency manager is backwards compatible with Celix 2.2.1
+set(CMAKE_CXX_STANDARD 11)
+add_executable(test_dep_man_with_cxx11
+        src/DependencyManagerTestSuite.cc
+)
+target_link_libraries(test_dep_man_with_cxx11 PRIVATE Celix::framework CURL::libcurl GTest::gtest GTest::gtest_main)
+setup_target_for_coverage(test_dep_man_with_cxx11 SCAN_DIR ..)
+
+
diff --git a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc
index d6ebe44..02f5263 100644
--- a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc
+++ b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc
@@ -158,7 +158,7 @@ TEST_F(CxxBundleContextTestSuite, UseServicesTest) {
 TEST_F(CxxBundleContextTestSuite, UseServicesWithFilterTest) {
     auto svc = std::make_shared<CInterface>(CInterface{nullptr, nullptr});
     auto svcReg1 = ctx->registerService<CInterface>(svc).build();
-    auto svcReg2 = ctx->registerService<CInterface>(svc).addProperty("key", "val1").build();
+    auto svcReg2 = ctx->registerService<CInterface>(svc).setProperties(celix::Properties{{"key", "val1"}}).build();
     auto svcReg3 = ctx->registerService<CInterface>(svc).addProperty("key", "val2").build();
 
     EXPECT_EQ(3, ctx->useServices<CInterface>().build());
@@ -169,7 +169,7 @@ TEST_F(CxxBundleContextTestSuite, UseServicesWithFilterTest) {
     celix::Filter f{"(key=val2)"};
     EXPECT_EQ(1, ctx->useServices<CInterface>().setFilter(f).build());
 
-    EXPECT_THROW(ctx->useServices<CInterface>().setFilter(celix::Filter{"bla"}).build(), celix::Exception);
+    EXPECT_THROW(ctx->useServices<CInterface>().setFilter(celix::Filter{"bla"}).build(), celix::FilterException);
 }
 
 
@@ -325,6 +325,7 @@ TEST_F(CxxBundleContextTestSuite, TrackBundlesTest) {
     };
 
     auto tracker = ctx->trackBundles()
+            .includeFrameworkBundleInCallback()
             .addOnInstallCallback(cb)
             .addOnStartCallback(cb)
             .addOnStopCallback(cb)
@@ -332,28 +333,30 @@ TEST_F(CxxBundleContextTestSuite, TrackBundlesTest) {
     tracker->wait();
     EXPECT_EQ(celix::TrackerState::OPEN, tracker->getState());
     EXPECT_TRUE(tracker->isOpen());
-    EXPECT_EQ(0, count.load());
+    EXPECT_EQ(2, count.load()); //count 1x install, 1x start is from the framework bundle
 
     long bndId = ctx->installBundle("non-existing");
     EXPECT_EQ(-1, bndId); //not installed
+    EXPECT_EQ(2, count.load());
 
     long bndId1 = ctx->installBundle(TEST_BND1_LOC);
     EXPECT_GE(bndId1, 0);
-    EXPECT_EQ(2, count.load()); // 1x install, 1x start
+    EXPECT_EQ(4, count.load()); // 2x install, 2x start
 
     long bndId2 = ctx->installBundle(TEST_BND2_LOC, false);
     EXPECT_GE(bndId1, 0);
-    EXPECT_EQ(3, count.load()); // 2x install, 1x start
+    EXPECT_EQ(5, count.load()); // 3x install, 2x start
     ctx->startBundle(bndId2);
-    EXPECT_EQ(4, count.load()); // 2x install, 2x start
+    EXPECT_EQ(6, count.load()); // 3x install, 3x start
 
+    count = 0;
     ctx->uninstallBundle(bndId1);
-    EXPECT_EQ(5, count.load()); // 2x install, 2x start, 1x stop
+    EXPECT_EQ(1, count.load()); // 1x stop
 
     ctx->stopBundle(bndId2);
-    EXPECT_EQ(6, count.load()); // 2x install, 2x start, 2x stop
+    EXPECT_EQ(2, count.load()); // 2x stop
     ctx->startBundle(bndId2);
-    EXPECT_EQ(7, count.load()); // 2x install, 3x start, 2x stop
+    EXPECT_EQ(3, count.load()); // 1x start, 2x stop
 }
 
 TEST_F(CxxBundleContextTestSuite, OnRegisterAndUnregisterCallbacks) {
@@ -438,6 +441,7 @@ TEST_F(CxxBundleContextTestSuite, TrackServices) {
 
     auto metaTracker = ctx->trackServiceTrackers<TestInterface>()
             .addOnTrackerCreatedCallback([&count](const celix::ServiceTrackerInfo& info) {
+                EXPECT_GE(info.trackerOwnerBundleId, 0);
                 EXPECT_EQ(celix::typeName<TestInterface>(), info.serviceName);
                 count++;
             })
@@ -581,9 +585,18 @@ TEST_F(CxxBundleContextTestSuite, setServicesWithTrackerWhenMultipleRegistration
                 }
                 count++;
             })
+            .addSetWithOwner([&count](std::shared_ptr<TestInterface> /*svc*/, const std::shared_ptr<const celix::Properties>& props, const std::shared_ptr<const celix::Bundle>& bnd) {
+                if (props) {
+                    std::cout << "Setting svc with svc id " << props->getAsLong(celix::SERVICE_ID, -1) << std::endl;
+                    std::cout << "from bnd " << std::to_string(bnd->getId());
+                } else {
+                    std::cout << "Unsetting svc" << std::endl;
+                }
+                count++;
+            })
             .build();
     tracker->wait();
-    EXPECT_EQ(1, count.load()); //NOTE ensure that the service tracker only calls set once for opening tracker even if 3 service exists.
+    EXPECT_EQ(2, count.load()); //NOTE ensure that the service tracker only calls set once for opening tracker even if 3 service exists.
 
     count = 0;
     tracker->close();
@@ -591,7 +604,7 @@ TEST_F(CxxBundleContextTestSuite, setServicesWithTrackerWhenMultipleRegistration
 
     //TODO improve this. For now closing a tracker will inject other service before getting to nullptr.
     //Also look into why this is not happening in the C service tracker test.
-    EXPECT_EQ(3, count.load());
+    EXPECT_EQ(6, count.load());
 }
 
 TEST_F(CxxBundleContextTestSuite, WaitForAllEvents) {
@@ -653,4 +666,42 @@ TEST_F(CxxBundleContextTestSuite, CheckStandardServiceProperties) {
     EXPECT_TRUE(called);
 
     celix_bundleContext_unregisterService(ctx->getCBundleContext(), svcId);
-}
\ No newline at end of file
+}
+
+TEST_F(CxxBundleContextTestSuite, GetBundleInformation) {
+
+    EXPECT_EQ(ctx->getBundle().getSymbolicName(), std::string{"celix_framework"});
+    EXPECT_EQ(ctx->getBundle().getName(), std::string{"Celix Framework"});
+    EXPECT_EQ(ctx->getBundle().getGroup(), std::string{"Celix/Framework"});
+    EXPECT_EQ(ctx->getBundle().getDescription(), std::string{"The Celix Framework System Bundle"});
+
+    std::atomic<bool> startCalled{false};
+    auto bndTracker = ctx->trackBundles()
+        .addOnStartCallback([&startCalled](const celix::Bundle& bnd) {
+            EXPECT_EQ(bnd.getSymbolicName(), std::string{"simple_test_bundle1"});
+            EXPECT_EQ(bnd.getName(), std::string{"Simple Test Bundle"});
+            EXPECT_EQ(bnd.getGroup(), std::string{"test/group"});
+            EXPECT_EQ(bnd.getDescription(), std::string{"Test Description"});
+            startCalled = true;
+        })
+        .build();
+
+    long bndId1 = ctx->installBundle(TEST_BND1_LOC);
+    EXPECT_GE(bndId1, 0);
+    ctx->waitForEvents();
+    EXPECT_TRUE(startCalled);
+}
+
+class TestInterfaceWithStaticInfo {
+public:
+    static constexpr std::string_view NAME = "TestName";
+    static constexpr std::string_view VERSION = "1.2.3";
+};
+
+TEST_F(CxxBundleContextTestSuite, RegisterServiceWithNameAndVersionInfo) {
+
+    auto reg = ctx->registerService<TestInterfaceWithStaticInfo>(std::make_shared<TestInterfaceWithStaticInfo>())
+            .build();
+    EXPECT_EQ(reg->getServiceName(), "TestName");
+    EXPECT_EQ(reg->getServiceVersion(), "1.2.3");
+}
diff --git a/libs/framework/gtest/src/CxxFrameworkFactoryTestSuite.cc b/libs/framework/gtest/src/CxxFrameworkFactoryTestSuite.cc
index 8e1a62a..04ce44c 100644
--- a/libs/framework/gtest/src/CxxFrameworkFactoryTestSuite.cc
+++ b/libs/framework/gtest/src/CxxFrameworkFactoryTestSuite.cc
@@ -31,6 +31,7 @@ public:
 
 TEST_F(CxxFrameworkFactoryTestSuite, CreateDestroy) {
     auto fw = celix::createFramework();
+    EXPECT_TRUE(fw->getFrameworkBundleContext()->getBundle().isSystemBundle());
 }
 
 TEST_F(CxxFrameworkFactoryTestSuite, WaitForStop) {
diff --git a/libs/framework/gtest/src/CxxUtilsTestSuite.cc b/libs/framework/gtest/src/CxxUtilsTestSuite.cc
deleted file mode 100644
index 28d8594..0000000
--- a/libs/framework/gtest/src/CxxUtilsTestSuite.cc
+++ /dev/null
@@ -1,62 +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 <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/gtest/src/DependencyManagerTestSuite.cc b/libs/framework/gtest/src/DependencyManagerTestSuite.cc
index 672a2f4..8737fcd 100644
--- a/libs/framework/gtest/src/DependencyManagerTestSuite.cc
+++ b/libs/framework/gtest/src/DependencyManagerTestSuite.cc
@@ -197,7 +197,7 @@ TEST_F(DependencyManagerTestSuite, CxxDmGetInfo) {
 
     auto info = mng.getInfo();
     EXPECT_EQ(info.bndId, 0);
-    EXPECT_EQ(info.bndSymbolicName, "Framework");
+    EXPECT_EQ(info.bndSymbolicName, "celix_framework");
     EXPECT_EQ(info.components.size(), 0); //not build yet
 
     mng.build();
@@ -773,4 +773,46 @@ TEST_F(DependencyManagerTestSuite, ComponentContextShouldNotLeak) {
             .addContext(std::make_shared<std::vector<std::string>>())
             .build();
     //note should not leak mem
-}
\ No newline at end of file
+}
+
+#if __cplusplus >= 201703L //C++17 or higher
+
+class TestInterfaceWithStaticInfo {
+public:
+    static constexpr const char * const NAME = "TestName";
+    static constexpr const char * const VERSION = "1.2.3";
+};
+
+TEST_F(DependencyManagerTestSuite, ProvideInterfaceWithStaticInfo) {
+    class TestComponent : public TestInterfaceWithStaticInfo {};
+    celix::dm::DependencyManager dm{ctx};
+    auto& cmp = dm.createComponent<TestComponent>();
+    cmp.createProvidedService<TestInterfaceWithStaticInfo>();
+    cmp.build();
+    EXPECT_EQ(cmp.getState(), celix::dm::ComponentState::TRACKING_OPTIONAL);
+
+    auto info = dm.getInfo();
+    EXPECT_EQ(info.components[0].interfacesInfo[0].serviceName, "TestName");
+    auto& props = info.components[0].interfacesInfo[0].properties;
+    const auto it = props.find(celix::SERVICE_VERSION);
+    ASSERT_TRUE(it != props.end());
+    EXPECT_EQ(it->second, "1.2.3");
+}
+
+TEST_F(DependencyManagerTestSuite, CreateInterfaceWithStaticInfo) {
+    class TestComponent : public TestInterfaceWithStaticInfo {};
+    celix::dm::DependencyManager dm{ctx};
+    auto& cmp = dm.createComponent<TestComponent>();
+    cmp.addInterface<TestInterfaceWithStaticInfo>();
+    cmp.build();
+    EXPECT_EQ(cmp.getState(), celix::dm::ComponentState::TRACKING_OPTIONAL);
+
+    auto info = dm.getInfo();
+    EXPECT_EQ(info.components[0].interfacesInfo[0].serviceName, "TestName");
+    auto& props = info.components[0].interfacesInfo[0].properties;
+    const auto it = props.find(celix::SERVICE_VERSION);
+    ASSERT_TRUE(it != props.end());
+    EXPECT_EQ(it->second, "1.2.3");
+}
+
+#endif
\ No newline at end of file
diff --git a/libs/framework/gtest/subdir/CMakeLists.txt b/libs/framework/gtest/subdir/CMakeLists.txt
index 4956e3e..780305c 100644
--- a/libs/framework/gtest/subdir/CMakeLists.txt
+++ b/libs/framework/gtest/subdir/CMakeLists.txt
@@ -18,4 +18,5 @@
 add_celix_bundle(simple_test_bundle4 NO_ACTIVATOR VERSION 1.0.0)
 add_celix_bundle(simple_test_bundle5 NO_ACTIVATOR VERSION 1.0.0)
 
-add_library(sublib SHARED src/foo.c)
+add_library(sublib SHARED src/sublib.c)
+target_link_libraries(sublib PRIVATE Celix::framework)
diff --git a/libs/framework/gtest/subdir/src/foo.c b/libs/framework/gtest/subdir/src/sublib.c
similarity index 94%
rename from libs/framework/gtest/subdir/src/foo.c
rename to libs/framework/gtest/subdir/src/sublib.c
index 6f56eb7..d218f84 100644
--- a/libs/framework/gtest/subdir/src/foo.c
+++ b/libs/framework/gtest/subdir/src/sublib.c
@@ -19,8 +19,10 @@
 
 #include <stdio.h>
 
-void foo(void);
+void foo();
+char* get_bundle_name();
 
-void foo(void) {
+void foo() {
     printf("nop\n");
 }
+
diff --git a/libs/framework/include/bundle.h b/libs/framework/include/bundle.h
index 3ca545e..5cf8998 100644
--- a/libs/framework/include/bundle.h
+++ b/libs/framework/include/bundle.h
@@ -40,6 +40,9 @@ extern "C" {
 struct celix_bundle_activator;
 typedef struct celix_bundle_activator celix_bundle_activator_t;
 
+/**
+ * @brief Create system bundle
+ */
 FRAMEWORK_EXPORT celix_status_t bundle_create(celix_bundle_t **bundle);
 
 FRAMEWORK_EXPORT celix_status_t
diff --git a/libs/framework/include/celix/Bundle.h b/libs/framework/include/celix/Bundle.h
index a098b3c..9095842 100644
--- a/libs/framework/include/celix/Bundle.h
+++ b/libs/framework/include/celix/Bundle.h
@@ -25,6 +25,16 @@
 
 namespace celix {
 
+    enum class BundleState {
+        UNKNOWN,
+        UNINSTALLED,
+        INSTALLED,
+        RESOLVED,
+        STARTING,
+        STOPPING,
+        ACTIVE,
+    };
+
     /**
      * @brief An installed bundle in the Celix framework.
      *
@@ -35,20 +45,20 @@ namespace celix {
      */
     class Bundle {
     public:
-        explicit Bundle(celix_bundle_t* _cBnd) : cBnd{_cBnd, [](celix_bundle_t*){/*nop*/}}{}
+        explicit Bundle(celix_bundle_t* _cBnd) : cBnd{_cBnd, [](celix_bundle_t*){/*nop*/}} {}
 
         /**
          * @brief get the bundle id.
          * @return
          */
-        long getId() const { return celix_bundle_getId(cBnd.get()); }
+        [[nodiscard]] long getId() const { return celix_bundle_getId(cBnd.get()); }
 
         /**
          * @brief Get the absolute path for a entry path relative in the bundle cache.
          * @return The absolute entry path or an empty string if the bundle does not have the entry for the given
          * relative path.
          */
-        std::string getEntry(const std::string& path) const {
+        [[nodiscard]] std::string getEntry(const std::string& path) const {
             std::string result{};
             char* entry = celix_bundle_getEntry(cBnd.get(), path.c_str());
             if (entry != nullptr) {
@@ -57,6 +67,65 @@ namespace celix {
             }
             return result;
         }
+
+        /**
+         * @brief the symbolic name of the bundle.
+         */
+        [[nodiscard]] std::string getSymbolicName() const {
+            return std::string{celix_bundle_getSymbolicName(cBnd.get())};
+        }
+
+        /**
+         * @brief The name of the bundle.
+         */
+        [[nodiscard]] std::string getName() const {
+            return std::string{celix_bundle_getName(cBnd.get())};
+        }
+
+        /**
+         * @brief The group of the bundle.
+         */
+        [[nodiscard]] std::string getGroup() const {
+            return std::string{celix_bundle_getGroup(cBnd.get())};
+        }
+
+        /**
+         * @brief The descriptoin of the bundle.
+         */
+        [[nodiscard]] std::string getDescription() const {
+            return std::string{celix_bundle_getDescription(cBnd.get())};
+        }
+
+        /**
+         * @brief The current bundle state.
+         */
+        [[nodiscard]] celix::BundleState getState() const {
+            auto cState = celix_bundle_getState(cBnd.get());
+            switch (cState) {
+                case OSGI_FRAMEWORK_BUNDLE_UNINSTALLED:
+                    return BundleState::UNINSTALLED;
+                case OSGI_FRAMEWORK_BUNDLE_INSTALLED:
+                    return BundleState::INSTALLED;
+                case OSGI_FRAMEWORK_BUNDLE_RESOLVED:
+                    return BundleState::RESOLVED;
+                case OSGI_FRAMEWORK_BUNDLE_STARTING:
+                    return BundleState::STARTING;
+                case OSGI_FRAMEWORK_BUNDLE_STOPPING:
+                    return BundleState::STOPPING;
+                case OSGI_FRAMEWORK_BUNDLE_ACTIVE:
+                    return BundleState::ACTIVE;
+                default:
+                    return BundleState::UNKNOWN;
+            }
+        }
+
+        /**
+         * @brief whether the bundle is the system (framework) bundle
+         * @return
+         */
+        [[nodiscard]] bool isSystemBundle() const {
+            return celix_bundle_isSystemBundle(cBnd.get());
+        }
     private:
         const std::shared_ptr<celix_bundle_t> cBnd;
     };
diff --git a/libs/framework/include/celix/BundleActivator.h b/libs/framework/include/celix/BundleActivator.h
index ecec6f6..ec7a046 100644
--- a/libs/framework/include/celix/BundleActivator.h
+++ b/libs/framework/include/celix/BundleActivator.h
@@ -25,73 +25,71 @@
 #include "celix_bundle_activator.h"
 #include "celix/BundleContext.h"
 
-namespace celix {
-    namespace impl {
+namespace celix::impl {
 
-        template<typename I>
-        struct BundleActivatorData {
-            long bndId;
-            std::shared_ptr<celix::BundleContext> ctx;
-            std::unique_ptr<I> bundleActivator;
-        };
+    template<typename I>
+    struct BundleActivatorData {
+        long bndId{};
+        std::shared_ptr<celix::BundleContext> ctx;
+        std::unique_ptr<I> bundleActivator;
+    };
 
 
-        template<typename I>
-        typename std::enable_if<std::is_constructible<I, std::shared_ptr<celix::BundleContext>>::value, celix_status_t>::type
-        createActivator(celix_bundle_context_t *cCtx, void **out) {
-            auto ctx = std::make_shared<celix::BundleContext>(cCtx);
-            auto act = std::unique_ptr<I>(new I{ctx});
-            auto *data = new BundleActivatorData<I>{ctx->getBundleId(), std::move(ctx), std::move(act)};
-            *out = (void *) data;
-            return CELIX_SUCCESS;
-        }
+    template<typename I>
+    typename std::enable_if<std::is_constructible<I, std::shared_ptr<celix::BundleContext>>::value, celix_status_t>::type
+    createActivator(celix_bundle_context_t *cCtx, void **out) {
+        auto ctx = std::make_shared<celix::BundleContext>(cCtx);
+        auto act = std::unique_ptr<I>(new I{ctx});
+        auto *data = new BundleActivatorData<I>{ctx->getBundleId(), std::move(ctx), std::move(act)};
+        *out = (void *) data;
+        return CELIX_SUCCESS;
+    }
 
-        template<typename I>
-        typename std::enable_if<std::is_constructible<I, std::shared_ptr<celix::dm::DependencyManager>>::value, celix_status_t>::type
-        createActivator(celix_bundle_context_t *cCtx, void **out) {
-            auto ctx = std::make_shared<celix::BundleContext>(cCtx);
-            auto dm = ctx->getDependencyManager();
-            auto act = std::unique_ptr<I>(new I{dm});
-            dm->start();
-            auto *data = new BundleActivatorData<I>{ctx->getBundleId(), std::move(ctx), std::move(act)};
-            *out = (void *) data;
-            return CELIX_SUCCESS;
-        }
+    template<typename I>
+    typename std::enable_if<std::is_constructible<I, std::shared_ptr<celix::dm::DependencyManager>>::value, celix_status_t>::type
+    createActivator(celix_bundle_context_t *cCtx, void **out) {
+        auto ctx = std::make_shared<celix::BundleContext>(cCtx);
+        auto dm = ctx->getDependencyManager();
+        auto act = std::unique_ptr<I>(new I{dm});
+        dm->start();
+        auto *data = new BundleActivatorData<I>{ctx->getBundleId(), std::move(ctx), std::move(act)};
+        *out = (void *) data;
+        return CELIX_SUCCESS;
+    }
 
-        template<typename T>
-        void waitForExpired(long bndId, std::weak_ptr<celix::BundleContext>& weakCtx, const char* name, std::weak_ptr<T>& observe) {
-            auto start = std::chrono::system_clock::now();
-            while (!observe.expired()) {
-                auto now = std::chrono::system_clock::now();
-                auto durationInSec = std::chrono::duration_cast<std::chrono::seconds>(now - start);
-                if (durationInSec > std::chrono::seconds{5}) {
-                    auto msg =  std::string{"Cannot destroy bundle "} + std::to_string(bndId) + ". " + name + " is still in use. std::shared_ptr use count is " + std::to_string(observe.use_count()) + "\n";
-                    auto ctx = weakCtx.lock();
-                    if (ctx) {
-                        ctx->logWarn(msg.c_str());
-                    } else {
-                        std::cout << msg;
-                    }
-                    start = now;
+    template<typename T>
+    void waitForExpired(long bndId, std::weak_ptr<celix::BundleContext>& weakCtx, const char* name, std::weak_ptr<T>& observe) {
+        auto start = std::chrono::system_clock::now();
+        while (!observe.expired()) {
+            auto now = std::chrono::system_clock::now();
+            auto durationInSec = std::chrono::duration_cast<std::chrono::seconds>(now - start);
+            if (durationInSec > std::chrono::seconds{5}) {
+                auto msg =  std::string{"Cannot destroy bundle "} + std::to_string(bndId) + ". " + name + " is still in use. std::shared_ptr use count is " + std::to_string(observe.use_count()) + "\n";
+                auto ctx = weakCtx.lock();
+                if (ctx) {
+                    ctx->logWarn(msg.c_str());
+                } else {
+                    std::cout << msg;
                 }
-                std::this_thread::sleep_for(std::chrono::milliseconds{50});
+                start = now;
             }
+            std::this_thread::sleep_for(std::chrono::milliseconds{50});
         }
+    }
 
-        template<typename I>
-        celix_status_t destroyActivator(void *userData) {
-            auto *data = static_cast<BundleActivatorData<I> *>(userData);
-            data->bundleActivator.reset();
-            data->ctx->getDependencyManager()->clear();
+    template<typename I>
+    celix_status_t destroyActivator(void *userData) {
+        auto *data = static_cast<BundleActivatorData<I> *>(userData);
+        data->bundleActivator.reset();
+        data->ctx->getDependencyManager()->clear();
 
-            auto bndId = data->bndId;
-            std::weak_ptr<celix::BundleContext> ctx = data->ctx;
-            std::weak_ptr<celix::dm::DependencyManager> dm = data->ctx->getDependencyManager();
-            delete data;
-            waitForExpired(bndId, ctx, "celix::BundleContext", ctx);
-            waitForExpired(bndId, ctx, "celix::dm::DependencyManager", dm);
-            return CELIX_SUCCESS;
-        }
+        auto bndId = data->bndId;
+        std::weak_ptr<celix::BundleContext> ctx = data->ctx;
+        std::weak_ptr<celix::dm::DependencyManager> dm = data->ctx->getDependencyManager();
+        delete data;
+        waitForExpired(bndId, ctx, "celix::BundleContext", ctx);
+        waitForExpired(bndId, ctx, "celix::dm::DependencyManager", dm);
+        return CELIX_SUCCESS;
     }
 }
 
diff --git a/libs/framework/include/celix/BundleContext.h b/libs/framework/include/celix/BundleContext.h
index 75e5510..66c79a0 100644
--- a/libs/framework/include/celix/BundleContext.h
+++ b/libs/framework/include/celix/BundleContext.h
@@ -377,7 +377,7 @@ namespace celix {
          * @brief List the installed and started bundle ids.
          * The bundle ids does not include the framework bundle (bundle id celix::FRAMEWORK_BUNDLE_ID).
          */
-        std::vector<long> listBundleIds() const {
+        [[nodiscard]] std::vector<long> listBundleIds() const {
             std::vector<long> result{};
             auto* ids = celix_bundleContext_listBundles(cCtx.get());
             result.reserve(celix_arrayList_size(ids));
@@ -399,7 +399,7 @@ namespace celix {
          * @param defaultVal The default value to use if the property is not found.
          * @return The config property value for the provided key or the provided defaultValue is the name is not found.
          */
-        std::string getConfigProperty(const std::string& name, const std::string& defaultValue) const {
+        [[nodiscard]] std::string getConfigProperty(const std::string& name, const std::string& defaultValue) const {
             return std::string{celix_bundleContext_getProperty(cCtx.get(), name.c_str(), defaultValue.c_str())};
         }
 
@@ -415,7 +415,7 @@ namespace celix {
          * @return The config property value (as long) for the provided key or the provided defaultValue is the name
          * is not found or not a valid long.
          */
-        long getConfigPropertyAsLong(const std::string& name, long defaultValue) const {
+        [[nodiscard]] long getConfigPropertyAsLong(const std::string& name, long defaultValue) const {
             return celix_bundleContext_getPropertyAsLong(cCtx.get(), name.c_str(), defaultValue);
         }
 
@@ -431,7 +431,7 @@ namespace celix {
          * @return The config property value (as double) for the provided key or the provided defaultValue is the name
          * is not found or not a valid double.
          */
-        double getConfigPropertyAsDouble(const std::string& name, double defaultValue) const {
+        [[nodiscard]] double getConfigPropertyAsDouble(const std::string& name, double defaultValue) const {
             return celix_bundleContext_getPropertyAsDouble(cCtx.get(), name.c_str(), defaultValue);
         }
 
@@ -449,28 +449,28 @@ namespace celix {
          * @return The config property value (as boolean) for the provided key or the provided defaultValue is the name
          * is not found or not a valid boolean.
          */
-        long getConfigPropertyAsBool(const std::string& name, bool defaultValue) const {
+        [[nodiscard]] long getConfigPropertyAsBool(const std::string& name, bool defaultValue) const {
             return celix_bundleContext_getPropertyAsBool(cCtx.get(), name.c_str(), defaultValue);
         }
 
         /**
          * @brief Get the bundle of this bundle context.
          */
-        const Bundle& getBundle() const {
+        [[nodiscard]] const Bundle& getBundle() const {
             return bnd;
         }
 
         /**
          * @brief Get the bundle id for the bundle of this bundle context
          */
-         long getBundleId() const {
+        [[nodiscard]] long getBundleId() const {
              return bnd.getId();
          }
 
         /**
          * @brief Get the Celix framework for this bundle context.
          */
-        std::shared_ptr<Framework> getFramework() const {
+        [[nodiscard]] std::shared_ptr<Framework> getFramework() const {
             auto* cFw = celix_bundleContext_getFramework(cCtx.get());
             auto fwCtx = std::make_shared<celix::BundleContext>(celix_framework_getFrameworkContext(cFw));
             return std::make_shared<Framework>(fwCtx, cFw);
@@ -479,7 +479,7 @@ namespace celix {
         /**
          * @brief Get the Celix dependency manager for this bundle context
          */
-        std::shared_ptr<dm::DependencyManager> getDependencyManager() const {
+        [[nodiscard]] std::shared_ptr<dm::DependencyManager> getDependencyManager() const {
             return dm;
         }
 
@@ -489,7 +489,7 @@ namespace celix {
          * @warning Try not the depend on the C API from a C++ bundle. If features are missing these should be added to
          * the C++ API.
          */
-        celix_bundle_context_t* getCBundleContext() const {
+        [[nodiscard]] celix_bundle_context_t* getCBundleContext() const {
             return cCtx.get();
         }
 
diff --git a/libs/framework/include/celix/Constants.h b/libs/framework/include/celix/Constants.h
index 8f84ccb..26c4d07 100644
--- a/libs/framework/include/celix/Constants.h
+++ b/libs/framework/include/celix/Constants.h
@@ -211,4 +211,12 @@ namespace celix {
      * If not specified ".cache" is used.
      */
     constexpr const char * const FRAMEWORK_CACHE_DIR = CELIX_FRAMEWORK_FRAMEWORK_STORAGE;
+
+    /**
+     * @brief Celix framework environment property (named "CELIX_FRAMEWORK_WARN_FOR_MISSING_SERVICE_VERSION")
+     * which configures the Celix framework to print warning when service are registered without a version property.
+     *
+     * Default is true.
+     */
+    constexpr const char * const WARN_FOR_MISSING_SERVICE_VERSION = CELIX_FRAMEWORK_WARN_FOR_MISSING_SERVICE_VERSION;
 }
\ No newline at end of file
diff --git a/libs/framework/include/celix/Exception.h b/libs/framework/include/celix/Exception.h
index 5f374d7..27aca02 100644
--- a/libs/framework/include/celix/Exception.h
+++ b/libs/framework/include/celix/Exception.h
@@ -11,11 +11,16 @@ namespace celix {
     public:
         explicit Exception(std::string msg) : w{std::move(msg)} {}
 
-        const char* what() const noexcept override {
+        Exception(const Exception&) = default;
+        Exception(Exception&&) = default;
+        Exception& operator=(const Exception&) = default;
+        Exception& operator=(Exception&&) = default;
+
+        [[nodiscard]] const char* what() const noexcept override {
             return w.c_str();
         }
     private:
-        const std::string w;
+        std::string w;
     };
 
 }
diff --git a/libs/framework/include/celix/Framework.h b/libs/framework/include/celix/Framework.h
index e557e55..f679722 100644
--- a/libs/framework/include/celix/Framework.h
+++ b/libs/framework/include/celix/Framework.h
@@ -44,14 +44,14 @@ namespace celix {
         /**
          * @brief Get the framework UUID.
          */
-        std::string getUUID() const {
+        [[nodiscard]] std::string getUUID() const {
             return std::string{celix_framework_getUUID(cFw.get())};
         }
 
         /**
          * @brief Get the bundle context for the framework.
          */
-        std::shared_ptr<celix::BundleContext> getFrameworkBundleContext() const {
+        [[nodiscard]] std::shared_ptr<celix::BundleContext> getFrameworkBundleContext() const {
             return fwCtx;
         }
 
@@ -105,7 +105,7 @@ namespace celix {
          * @warning Try not the depend on the C API from a C++ bundle. If features are missing these should be added to
          * the C++ API.
          */
-        celix_framework_t * getCFramework() const {
+        [[nodiscard]] celix_framework_t * getCFramework() const {
             return cFw.get();
         }
     private:
diff --git a/libs/framework/include/celix/FrameworkFactory.h b/libs/framework/include/celix/FrameworkFactory.h
index ad5effc..4f1f3a2 100644
--- a/libs/framework/include/celix/FrameworkFactory.h
+++ b/libs/framework/include/celix/FrameworkFactory.h
@@ -30,7 +30,7 @@ namespace celix {
     /**
      * @brief Create a new celix Framework instance.
      */
-    static std::shared_ptr<celix::Framework> createFramework(const celix::Properties& properties = {}) {
+    inline std::shared_ptr<celix::Framework> createFramework(const celix::Properties& properties = {}) {
         auto* copy = celix_properties_copy(properties.getCProperties());
         auto* cFw= celix_frameworkFactory_createFramework(copy);
         auto fwCtx = std::make_shared<celix::BundleContext>(celix_framework_getFrameworkContext(cFw));
diff --git a/libs/framework/include/celix/ServiceRegistrationBuilder.h b/libs/framework/include/celix/ServiceRegistrationBuilder.h
index dcbe087..b4798a6 100644
--- a/libs/framework/include/celix/ServiceRegistrationBuilder.h
+++ b/libs/framework/include/celix/ServiceRegistrationBuilder.h
@@ -23,6 +23,7 @@
 #include <vector>
 #include <memory>
 
+#include "celix/Utils.h"
 #include "celix/ServiceRegistration.h"
 
 namespace celix {
@@ -39,7 +40,7 @@ namespace celix {
         friend class BundleContext;
 
         //NOTE private to prevent move so that a build() call cannot be forgotten
-        ServiceRegistrationBuilder(ServiceRegistrationBuilder&&) = default;
+        ServiceRegistrationBuilder(ServiceRegistrationBuilder&&) noexcept = default;
     public:
         ServiceRegistrationBuilder(
                 std::shared_ptr<celix_bundle_context_t> _cCtx,
@@ -50,6 +51,7 @@ namespace celix {
                 cCtx{std::move(_cCtx)},
                 svc{std::move(_svc)},
                 name{std::move(_name)},
+                version{celix::typeVersion<I>()},
                 registerAsync{_registerAsync},
                 unregisterAsync{_unregisterAsync}{}
 
@@ -69,14 +71,14 @@ namespace celix {
          *
          * If a key is already present the value will be overridden.
          */
-        ServiceRegistrationBuilder& addProperty(std::string key, std::string value) { properties.set(std::move(key), std::move(value)); return *this; }
+        ServiceRegistrationBuilder& addProperty(const std::string& key, const std::string& value) { properties.set(key, value); return *this; }
 
         /**
          * @brief Add a property to the service properties.
          *
          * If a key is already present the value will be overridden.
          */
-        ServiceRegistrationBuilder& addProperty(std::string key, const char* value) { properties.set(std::move(key), std::string{value}); return *this; }
+        ServiceRegistrationBuilder& addProperty(const std::string& key, const char* value) { properties.set(key, std::string{value}); return *this; }
 
         /**
          * @brief Add a property to the service properties.
@@ -84,7 +86,7 @@ namespace celix {
          * If a key is already present the value will be overridden.
          */
         template<typename T>
-        ServiceRegistrationBuilder& addProperty(std::string key, T value) { properties.set(std::move(key), std::to_string(std::move(value))); return *this; }
+        ServiceRegistrationBuilder& addProperty(const std::string& key, T value) { properties.set(key, std::to_string(std::move(value))); return *this; }
 
         /**
          * @brief Set the service properties.
@@ -192,9 +194,9 @@ namespace celix {
         const std::shared_ptr<celix_bundle_context_t> cCtx;
         std::shared_ptr<I> svc;
         std::string name;
+        std::string version;
         bool registerAsync;
         bool unregisterAsync;
-        std::string version{};
         celix::Properties properties{};
         std::vector<std::function<void(ServiceRegistration&)>> onRegisteredCallbacks{};
         std::vector<std::function<void(ServiceRegistration&)>> onUnregisteredCallbacks{};
diff --git a/libs/framework/include/celix/TrackerBuilders.h b/libs/framework/include/celix/TrackerBuilders.h
index bed7f3c..f4acc0b 100644
--- a/libs/framework/include/celix/TrackerBuilders.h
+++ b/libs/framework/include/celix/TrackerBuilders.h
@@ -40,7 +40,7 @@ namespace celix {
         friend class BundleContext;
 
         //NOTE private to prevent move so that a build() call cannot be forgotten
-        ServiceTrackerBuilder(ServiceTrackerBuilder&&) = default;
+        ServiceTrackerBuilder(ServiceTrackerBuilder&&) noexcept = default;
     public:
         explicit ServiceTrackerBuilder(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string _name) :
                 cCtx{std::move(_cCtx)},
@@ -57,7 +57,7 @@ namespace celix {
          * Example:
          *      "(property_key=value)"
          */
-        ServiceTrackerBuilder& setFilter(std::string f) { filter = celix::Filter{std::move(f)}; return *this; }
+        ServiceTrackerBuilder& setFilter(const std::string& f) { filter = celix::Filter{f}; return *this; }
 
         /**
          * @brief Set filter to be used to matching services.
@@ -74,8 +74,7 @@ namespace celix {
          */
         template<typename F>
         ServiceTrackerBuilder& addAddCallback(F&& add) {
-            //TODO improve capture with move when for C++14 is available
-            addCallbacks.emplace_back([add](const std::shared_ptr<I>& svc, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&) {
+            addCallbacks.emplace_back([add = std::forward<F>(add)](const std::shared_ptr<I>& svc, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&) {
                 add(svc);
             });
             return *this;
@@ -92,8 +91,7 @@ namespace celix {
           */
         template<typename F>
         ServiceTrackerBuilder& addAddWithPropertiesCallback(F&& add) {
-            //TODO improve capture with forward when for C++14 is available
-            addCallbacks.emplace_back([add](const std::shared_ptr<I>& svc, const std::shared_ptr<const celix::Properties>& props, const std::shared_ptr<const celix::Bundle>&) {
+            addCallbacks.emplace_back([add = std::forward<F>(add)](const std::shared_ptr<I>& svc, const std::shared_ptr<const celix::Properties>& props, const std::shared_ptr<const celix::Bundle>&) {
                 add(svc, props);
             });
             return *this;
@@ -109,8 +107,7 @@ namespace celix {
          */
         template<typename F>
         ServiceTrackerBuilder& addAddWithOwnerCallback(F&& add) {
-            //TODO improve capture with forward when for C++14 is available
-            addCallbacks.emplace_back(std::move(add));
+            addCallbacks.emplace_back(std::forward<F>(add));
             return *this;
         }
 
@@ -124,8 +121,7 @@ namespace celix {
          */
         template<typename F>
         ServiceTrackerBuilder& addRemCallback(F&& remove) {
-            //TODO improve capture with move when for C++14 is available
-            remCallbacks.emplace_back([remove](const std::shared_ptr<I>& svc, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&) {
+            remCallbacks.emplace_back([remove = std::forward<F>(remove)](const std::shared_ptr<I>& svc, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&) {
                 remove(svc);
             });
             return *this;
@@ -141,8 +137,7 @@ namespace celix {
          */
         template<typename F>
         ServiceTrackerBuilder& addRemWithPropertiesCallback(F&& remove) {
-            //TODO improve capture with move when for C++14 is available
-            remCallbacks.emplace_back([remove](const std::shared_ptr<I>& svc, const std::shared_ptr<const celix::Properties>& props, const std::shared_ptr<const celix::Bundle>&) {
+            remCallbacks.emplace_back([remove = std::forward<F>(remove)](const std::shared_ptr<I>& svc, const std::shared_ptr<const celix::Properties>& props, const std::shared_ptr<const celix::Bundle>&) {
                 remove(svc, props);
             });
             return *this;
@@ -158,7 +153,7 @@ namespace celix {
          */
         template<typename F>
         ServiceTrackerBuilder& addRemWithOwnerCallback(F&& remove) {
-            remCallbacks.emplace_back(std::move(remove));
+            remCallbacks.emplace_back(std::forward<F>(remove));
             return *this;
         }
 
@@ -173,8 +168,7 @@ namespace celix {
          */
         template<typename F>
         ServiceTrackerBuilder& addSetCallback(F&& set) {
-            //TODO improve capture with move when for C++14 is available
-            setCallbacks.emplace_back([set](const std::shared_ptr<I>& svc, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&) {
+            setCallbacks.emplace_back([set = std::forward<F>(set)](const std::shared_ptr<I>& svc, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&) {
                 set(svc);
             });
             return *this;
@@ -191,9 +185,8 @@ namespace celix {
          */
         template<typename F>
         ServiceTrackerBuilder& addSetWithPropertiesCallback(F&& set) {
-            //TODO improve capture with move when for C++14 is available
-            setCallbacks.emplace_back([set](const std::shared_ptr<I>& svc, const std::shared_ptr<const celix::Properties>& props, const std::shared_ptr<const celix::Bundle>&) {
-                set(svc, std::move(props));
+            setCallbacks.emplace_back([set = std::forward<F>(set)](const std::shared_ptr<I>& svc, const std::shared_ptr<const celix::Properties>& props, const std::shared_ptr<const celix::Bundle>&) {
+                set(svc, props);
             });
             return *this;
         }
@@ -209,18 +202,10 @@ namespace celix {
          */
         template<typename F>
         ServiceTrackerBuilder& addSetWithOwner(F&& set) {
-            setCallbacks.emplace_back(std::move(set));
+            setCallbacks.emplace_back(std::forward<F>(set));
             return *this;
         }
 
-//        /**
-//         * TODO
-//         */
-//        ServiceTrackerBuilder& addUpdateCallback(std::function<void(std::vector<std::shared_ptr<I>>)> update) {
-//            //TODO update -> vector of ordered (svc rank) services
-//            return *this;
-//        }
-
         /**
          * @brief "Builds" the service tracker and returns a ServiceTracker.
          *
diff --git a/libs/framework/include/celix/Trackers.h b/libs/framework/include/celix/Trackers.h
index 5e8002a..bcd849d 100644
--- a/libs/framework/include/celix/Trackers.h
+++ b/libs/framework/include/celix/Trackers.h
@@ -368,9 +368,9 @@ namespace celix {
         }
     protected:
         struct SvcEntry {
-            SvcEntry(long _svcId, long _svcRanking, const std::shared_ptr<I> _svc,
-                     const std::shared_ptr<const celix::Properties> _properties,
-                     const std::shared_ptr<const celix::Bundle> _owner) : svcId(_svcId), svcRanking(_svcRanking),
+            SvcEntry(long _svcId, long _svcRanking, std::shared_ptr<I> _svc,
+                     std::shared_ptr<const celix::Properties> _properties,
+                     std::shared_ptr<const celix::Bundle> _owner) : svcId(_svcId), svcRanking(_svcRanking),
                                                                           svc(std::move(_svc)),
                                                                           properties(std::move(_properties)),
                                                                           owner(std::move(_owner)) {}
@@ -527,15 +527,16 @@ namespace celix {
             }
         }
 
-        const std::chrono::milliseconds warningTimoutForNonExpiredSvcObject{1000}; //TODO make configureable with buidler
+        const std::chrono::milliseconds warningTimoutForNonExpiredSvcObject{1000};
 
         const std::vector<std::function<void(const std::shared_ptr<I>&, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&)>> setCallbacks;
         const std::vector<std::function<void(const std::shared_ptr<I>&, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&)>> addCallbacks;
         const std::vector<std::function<void(const std::shared_ptr<I>&, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&)>> remCallbacks;
 
-        const std::vector<std::function<void(const std::vector<std::shared_ptr<I>>)>> updateCallbacks{}; //TODO add to ctor, builder and test
-        const std::vector<std::function<void(const std::vector<std::pair<std::shared_ptr<I>, std::shared_ptr<const celix::Properties>>>)>> updateWithPropertiesCallbacks{}; //TODO add to ctor, builder and test
-        const std::vector<std::function<void(const std::vector<std::tuple<std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, std::shared_ptr<const celix::Bundle>>>)>> updateWithOwnerCallbacks{}; //TODO add to ctor, builder and test
+        //NOTE update callbacks cannot yet be configured
+        const std::vector<std::function<void(const std::vector<std::shared_ptr<I>>)>> updateCallbacks{};
+        const std::vector<std::function<void(const std::vector<std::pair<std::shared_ptr<I>, std::shared_ptr<const celix::Properties>>>)>> updateWithPropertiesCallbacks{};
+        const std::vector<std::function<void(const std::vector<std::tuple<std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, std::shared_ptr<const celix::Bundle>>>)>> updateWithOwnerCallbacks{};
 
         struct SvcEntryCompare {
             bool operator() (const std::shared_ptr<SvcEntry>& a, const std::shared_ptr<SvcEntry>& b) const {
@@ -618,21 +619,21 @@ namespace celix {
             opts.callbackHandle = this;
             opts.onInstalled = [](void *handle, const celix_bundle_t *cBnd) {
                 auto tracker = static_cast<BundleTracker *>(handle);
-                auto bnd = celix::Bundle{const_cast<celix_bundle_t *>(cBnd)};
+                celix::Bundle bnd{const_cast<celix_bundle_t *>(cBnd)};
                 for (const auto& cb : tracker->onInstallCallbacks) {
                     cb(bnd);
                 }
             };
             opts.onStarted = [](void *handle, const celix_bundle_t *cBnd) {
                 auto tracker = static_cast<BundleTracker *>(handle);
-                auto bnd = celix::Bundle{const_cast<celix_bundle_t *>(cBnd)};
+                celix::Bundle bnd{const_cast<celix_bundle_t *>(cBnd)};
                 for (const auto& cb : tracker->onStartCallbacks) {
                     cb(bnd);
                 }
             };
             opts.onStopped = [](void *handle, const celix_bundle_t *cBnd) {
                 auto tracker = static_cast<BundleTracker *>(handle);
-                auto bnd = celix::Bundle{const_cast<celix_bundle_t *>(cBnd)};
+                celix::Bundle bnd{const_cast<celix_bundle_t *>(cBnd)};
                 for (const auto& cb : tracker->onStopCallbacks) {
                     cb(bnd);
                 }
diff --git a/libs/framework/include/celix/UseServiceBuilder.h b/libs/framework/include/celix/UseServiceBuilder.h
index 1bf9ddb..c504cfd 100644
--- a/libs/framework/include/celix/UseServiceBuilder.h
+++ b/libs/framework/include/celix/UseServiceBuilder.h
@@ -52,7 +52,7 @@ namespace celix {
         friend class BundleContext;
 
         //NOTE private to prevent move so that a build() call cannot be forgotten
-        UseServiceBuilder(UseServiceBuilder&&) = default;
+        UseServiceBuilder(UseServiceBuilder&&) noexcept = default;
     public:
         explicit UseServiceBuilder(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string _name, bool _useSingleService = true) :
                 cCtx{std::move(_cCtx)},
@@ -71,7 +71,7 @@ namespace celix {
          * Example:
          *      "(property_key=value)"
          */
-        UseServiceBuilder& setFilter(std::string f) { filter = celix::Filter{std::move(f)}; return *this; }
+        UseServiceBuilder& setFilter(const std::string& f) { filter = celix::Filter{f}; return *this; }
 
         /**
          * @brief Set filter to be used to matching services.
@@ -155,7 +155,7 @@ namespace celix {
             opts.useWithOwner = [](void* data, void *voidSvc, const celix_properties_t* cProps, const celix_bundle_t* cBnd) {
                 auto* builder = static_cast<UseServiceBuilder<I>*>(data);
                 auto* svc = static_cast<I*>(voidSvc);
-                const Bundle bnd = Bundle{const_cast<celix_bundle_t*>(cBnd)};
+                const Bundle bnd{const_cast<celix_bundle_t*>(cBnd)};
                 auto props = celix::Properties::wrap(cProps);
                 for (const auto& func : builder->callbacks) {
                     func(*svc);
diff --git a/libs/framework/include/celix/Utils.h b/libs/framework/include/celix/Utils.h
deleted file mode 100644
index db00a03..0000000
--- a/libs/framework/include/celix/Utils.h
+++ /dev/null
@@ -1,122 +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 <string.h>
-#include <iostream>
-#include <vector>
-
-//NOTE based on has_rtti.cpp
-#if defined(__clang__)
-#if __has_feature(cxx_rtti)
-#define CELIX_RTTI_ENABLED
-#endif
-#elif defined(__GNUG__)
-#if defined(__GXX_RTTI)
-#define CELIX_RTTI_ENABLED
-#endif
-#elif defined(_MSC_VER)
-#if defined(_CPPRTTI)
-#define CELIX_RTTI_ENABLED
-#endif
-#endif
-
-//FORCE DISABLE RTTI
-//TODO #323 add test CI job to test rtti based type name infer
-#undef CELIX_RTTI_ENABLED
-
-#ifdef CELIX_RTTI_ENABLED
-#include <cxxabi.h>
-#endif
-
-namespace celix {
-    /**
-     * @brief Returns the deferred type name for the template I
-     */
-    template<typename INTERFACE_TYPENAME>
-    std::string typeName() {
-        std::string result;
-
-#ifdef CELIX_RTTI_ENABLED
-        result = typeid(INTERFACE_TYPENAME).name();
-        int status = 0;
-        char* demangled_name {abi::__cxa_demangle(result.c_str(), NULL, NULL, &status)};
-        if(status == 0) {
-            result = std::string{demangled_name};
-            free(demangled_name);
-        }
-#else
-        const char *templateStr = "INTERFACE_TYPENAME = ";
-        const size_t templateStrLen = strlen(templateStr);
-
-        result = __PRETTY_FUNCTION__; //USING pretty function to retrieve the filled in template argument without using typeid()
-        size_t bpos = result.find(templateStr) + templateStrLen; //find begin pos after INTERFACE_TYPENAME = entry
-        size_t epos = bpos;
-        while (isalnum(result[epos]) || result[epos] == '_' || result[epos] == ':') {
-            epos += 1;
-        }
-        size_t len = epos - bpos;
-        result = result.substr(bpos, len);
-#endif
-
-//        std::cout << "PRETTY IS '" << __PRETTY_FUNCTION__ << "'\n";
-//        std::cout << "RESULT IS '" << result << "'\n";
-
-        if (result.empty()) {
-            std::cerr << "Cannot infer type name in function call '" << __PRETTY_FUNCTION__ << "'\n'";
-        }
-
-
-        return result;
-    }
-
-    /**
-     * @brief Returns the deferred type name for the template I if the providedTypeName is empty.
-     *
-     * If the providedTypeName is not empty, the providedTypeName will be returned.
-     */
-    template<typename I>
-    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
diff --git a/libs/framework/include/celix/dm/Component_Impl.h b/libs/framework/include/celix/dm/Component_Impl.h
index da3ff5d..d860afa 100644
--- a/libs/framework/include/celix/dm/Component_Impl.h
+++ b/libs/framework/include/celix/dm/Component_Impl.h
@@ -59,7 +59,7 @@ Component<T>::Component(
         std::string uuid) : BaseComponent(
                 context,
                 cDepMan,
-                name.empty() ? celix::typeName<T>() : std::move(name),
+                celix::typeName<T>(name),
                 std::move(uuid)) {}
 
 template<class T>
@@ -93,6 +93,10 @@ Component<T>& Component<T>::addInterface(const std::string &version, const Prope
     if (serviceName.empty()) {
         std::cerr << "Cannot add interface, because type name could not be inferred. function: '"  << __PRETTY_FUNCTION__ << "'\n";
     }
+    auto svcVersion = celix::typeVersion<I>(version);
+    if (!svcVersion.empty()) {
+        properties[celix::SERVICE_VERSION] = std::move(svcVersion);
+    }
 
     return this->addInterfaceWithName<I>(serviceName, version, properties);
 }
@@ -163,7 +167,7 @@ Component<T>& Component<T>::remove(CServiceDependency<T,I>& dep) {
 
 template<class T>
 std::shared_ptr<Component<T>> Component<T>::create(celix_bundle_context_t *context, celix_dependency_manager_t* cDepMan, std::string name, std::string uuid) {
-    std::string cmpName = name.empty() ? celix::typeName<T>() : std::move(name);
+    std::string cmpName = celix::typeName<T>(name);
     return std::shared_ptr<Component<T>>{new Component<T>(context, cDepMan, std::move(cmpName), std::move(uuid)), [](Component<T>* cmp){
         //Using a callback of async destroy to ensure that the cmp instance is still exist while the
         //dm component is async disabled and destroyed.
@@ -405,6 +409,10 @@ ProvidedService<T, I> &Component<T>::createProvidedService(std::string serviceNa
 
     I* svcPtr = &this->getInstance();
     auto provide = std::make_shared<ProvidedService<T,I>>(cComponent(), serviceName, svcPtr, true);
+    auto svcVersion = celix::typeVersion<I>();
+    if (!svcVersion.empty()) {
+        provide->addProperty(celix::SERVICE_VERSION, std::move(svcVersion));
+    }
     std::lock_guard<std::mutex> lck{mutex};
     providedServices.push_back(provide);
     return *provide;
diff --git a/libs/framework/include/celix_bundle.h b/libs/framework/include/celix_bundle.h
index ae97ab2..53913eb 100644
--- a/libs/framework/include/celix_bundle.h
+++ b/libs/framework/include/celix_bundle.h
@@ -30,21 +30,21 @@ extern "C" {
 #endif
 
 /**
- * Returns the bundle id.
+ * @brief Returns the bundle id.
  * @param bnd The bundle
  * @return The bundle id or < 0 if something went wrong.
  */
 long celix_bundle_getId(const celix_bundle_t *bnd);
 
 /**
- * Returns the bundle state.
+ * @brief Returns the bundle state.
  * @param bnd The bundle
  * @return The bundle state or OSGI_FRAMEWORK_BUNDLE_UNKNOWN if something went wrong.
  */
 celix_bundle_state_e celix_bundle_getState(const celix_bundle_t *bnd);
 
 /**
- * Returns a the use-able entry path for the provided relative path to a bundle resource.
+ * @brief Returns a the use-able entry path for the provided relative path to a bundle resource.
  *
  * For example if there is a resource entry in the bundle at path 'META-INF/descriptors/foo.descriptor` this call
  * will return a absolute or relative path to the extracted location of the bundle resource, e.g.:
@@ -57,19 +57,31 @@ celix_bundle_state_e celix_bundle_getState(const celix_bundle_t *bnd);
 char* celix_bundle_getEntry(const celix_bundle_t* bnd, const char *path);
 
 /**
- * Returns the group of the bundle. Groups are used to order bundles.
+ * @brief Returns the group of the bundle. Groups are used to order bundles.
  * Note the return value is valid as long as the bundle is installed.
  */
 const char* celix_bundle_getGroup(const celix_bundle_t *bnd);
 
 /**
- * Returns the symbolic name of the bundle.
+ * @brief Returns the symbolic name of the bundle.
  * Note the return value is valid as long as the bundle is installed.
  */
 const char* celix_bundle_getSymbolicName(const celix_bundle_t *bnd);
 
 /**
- * Returns whether the bundle is the system bundle.
+ * @brief Returns the name of the bundle.
+ * Note the return value is valid as long as the bundle is installed.
+ */
+const char* celix_bundle_getName(const celix_bundle_t* bnd);
+
+/**
+ * @brief Returns the description of the bundle.
+ * Note the return value is valid as long as the bundle is installed.
+ */
+const char* celix_bundle_getDescription(const celix_bundle_t* bnd);
+
+/**
+ * @brief Returns whether the bundle is the system bundle.
  */
 bool celix_bundle_isSystemBundle(const celix_bundle_t *bnd);
 
diff --git a/libs/framework/include/celix_bundle_activator.h b/libs/framework/include/celix_bundle_activator.h
index 623319b..7070f25 100644
--- a/libs/framework/include/celix_bundle_activator.h
+++ b/libs/framework/include/celix_bundle_activator.h
@@ -29,7 +29,8 @@ extern "C" {
 #endif
 
 /**
- * Called when this bundle is started so the bundle can create an instance for its activator.
+ * @brief Called when this bundle is started so the bundle can create an instance for its activator.
+ *
  * The framework does not assume any type for the activator instance, this is implementation specific.
  * The activator instance is handle as a void pointer by the framework, the implementation must cast it to the
  * implementation specific type.
@@ -45,9 +46,10 @@ extern "C" {
 celix_status_t celix_bundleActivator_create(celix_bundle_context_t *ctx, void **userData);
 
 /**
- * Called when this bundle is started so the Framework can perform the bundle-specific activities necessary
- * to start this bundle. This method can be used to register services or to allocate any resources that this
- * bundle needs.
+ * @brief Called when this bundle is started so the Framework can perform the bundle-specific activities necessary
+ * to start this bundle.
+ *
+ * This method can be used to register services or to allocate any resources that this bundle needs.
  *
  * <p>
  * This method must complete and return to its caller in a timely manner.
@@ -63,8 +65,10 @@ celix_status_t celix_bundleActivator_create(celix_bundle_context_t *ctx, void **
 celix_status_t celix_bundleActivator_start(void *userData, celix_bundle_context_t *ctx);
 
 /**
- * Called when this bundle is stopped so the Framework can perform the bundle-specific activities necessary
- * to stop the bundle. In general, this method should undo the work that the <code>bundleActivator_start()</code>
+ * @brief Called when this bundle is stopped so the Framework can perform the bundle-specific activities necessary
+ * to stop the bundle.
+ *
+ * In general, this method should undo the work that the <code>bundleActivator_start()</code>
  * function started. There should be no active threads that were started by this bundle when this bundle returns.
  * A stopped bundle must not call any Framework objects.
  *
@@ -82,8 +86,9 @@ celix_status_t celix_bundleActivator_start(void *userData, celix_bundle_context_
 celix_status_t celix_bundleActivator_stop(void *userData, celix_bundle_context_t *ctx);
 
 /**
- * Called when this bundle is stopped so the bundle can destroy the instance of its activator. In general, this
- * method should undo the work that the <code>bundleActivator_create()</code> function initialized.
+ * @brief Called when this bundle is stopped so the bundle can destroy the instance of its activator.
+ *
+ * In general, this method should undo the work that the <code>bundleActivator_create()</code> function initialized.
  *
  * <p>
  * This method must complete and return to its caller in a timely manner.
@@ -98,9 +103,14 @@ celix_status_t celix_bundleActivator_stop(void *userData, celix_bundle_context_t
  */
 celix_status_t celix_bundleActivator_destroy(void *userData, celix_bundle_context_t* ctx);
 
+/**
+ * @brief Returns the C bundle context.
+ */
+celix_bundle_context_t* celix_bundleActivator_getBundleContext();
 
 /**
- * This macro generates the required bundle activator functions for C.
+ * @brief This macro generates the required bundle activator functions for C.
+ *
  * This can be used to more type safe bundle activator entries.
  *
  * The macro will create the following bundle activator functions:
@@ -113,7 +123,19 @@ celix_status_t celix_bundleActivator_destroy(void *userData, celix_bundle_contex
  * @param stop the activator actStop function with the following signature: celix_status_t (*)(<actType>*, celix_bundle_context_t*).
  */
 #define CELIX_GEN_BUNDLE_ACTIVATOR(actType, actStart, actStop)                                                         \
-celix_status_t celix_bundleActivator_create(celix_bundle_context_t *ctx __attribute__((unused)), void **userData) {    \
+                                                                                                                       \
+static celix_bundle_context_t* celix_currentBundleContext = NULL;                                                      \
+                                                                                                                       \
+celix_bundle_context_t* celix_bundleActivator_getBundleContext() {                                                     \
+    /*celix_bundle_context_t* ctx;                                                                                     \
+    __atomic_load(&g_celix_currentBundleContext, &ctx, __ATOMIC_ACQUIRE);                                              \
+    return ctx;*/                                                                                                      \
+    return celix_currentBundleContext;                                                                                 \
+}                                                                                                                      \
+                                                                                                                       \
+celix_status_t celix_bundleActivator_create(celix_bundle_context_t *ctx, void **userData) {                            \
+    /*__atomic_store(&celix_currentBundleContext, &ctx, __ATOMIC_RELAXED);*/                                           \
+    celix_currentBundleContext = ctx;                                                                                  \
     celix_status_t status = CELIX_SUCCESS;                                                                             \
     actType *data = (actType*)calloc(1, sizeof(*data));                                                                \
     if (data != NULL) {                                                                                                \
diff --git a/libs/framework/include/celix_bundle_context.h b/libs/framework/include/celix_bundle_context.h
index c6836a5..dd0363c 100644
--- a/libs/framework/include/celix_bundle_context.h
+++ b/libs/framework/include/celix_bundle_context.h
@@ -40,9 +40,8 @@ extern "C" {
 #define OPTS_INIT
 #endif
 
-
 /**
- * Register a service to the Celix framework.
+ * @brief Register a service to the Celix framework.
  *
  * The service will be registered async on the Celix event loop thread. This means that service registration is (probably)
  * not yet concluded when this function returns, but is added to the event loop.
@@ -58,7 +57,8 @@ extern "C" {
 long celix_bundleContext_registerServiceAsync(celix_bundle_context_t *ctx, void *svc, const char *serviceName, celix_properties_t *properties);
 
 /**
- * Register a service to the Celix framework.
+ * @brief Register a service to the Celix framework.
+ *
  * Note: Please use the celix_bundleContext_registerServiceAsync instead.
  *
  * @param ctx The bundle context
@@ -70,7 +70,8 @@ long celix_bundleContext_registerServiceAsync(celix_bundle_context_t *ctx, void
 long celix_bundleContext_registerService(celix_bundle_context_t *ctx, void *svc, const char *serviceName, celix_properties_t *properties); //__attribute__((deprecated("Use celix_bundleContext_registerServiceAsync instead!")));
 
 /**
- * Register a service factory in the framework (for the C language).
+ * @brief Register a service factory in the framework.
+ *
  * The service factory will be called for every bundle requesting/de-requesting a service. This gives the provider the
  * option to create bundle specific service instances.
  *
@@ -93,7 +94,8 @@ long celix_bundleContext_registerService(celix_bundle_context_t *ctx, void *svc,
 long celix_bundleContext_registerServiceFactoryAsync(celix_bundle_context_t *ctx, celix_service_factory_t *factory, const char *serviceName, celix_properties_t *props);
 
 /**
- * Register a service factory in the framework (for the C language).
+ * @brief Register a service factory in the framework.
+ *
  * The service factory will be called for every bundle requesting/de-requesting a service. This gives the provider the
  * option to create bundle specific service instances.
  * Note: Please use the celix_bundleContext_registerServiceFactoryAsync instead.
@@ -112,11 +114,13 @@ long celix_bundleContext_registerServiceFactoryAsync(celix_bundle_context_t *ctx
 long celix_bundleContext_registerServiceFactory(celix_bundle_context_t *ctx, celix_service_factory_t *factory, const char *serviceName, celix_properties_t *props); //__attribute__((deprecated("Use celix_bundleContext_registerServiceFactoryAsync instead!")));
 
 /**
- * Service Registration Options when registering services to the Celix framework.
+ * @brief Service Registration Options when registering services to the Celix framework.
  */
 typedef struct celix_service_registration_options {
     /**
-     * The service pointer. The actual pointer to the service. For C this is normally a pointer to a struct
+     * @brief The service pointer.
+     *
+     * The actual pointer to the service. For C this is normally a pointer to a struct
      * with function pointers, but theoretically this can be a pointer to anything (e.g. a pointer to a single function,
      * or a pointer to a C++ interface implementation, or just a pointer to a data structure).
      *
@@ -125,7 +129,8 @@ typedef struct celix_service_registration_options {
     void *svc OPTS_INIT;
 
     /**
-     * The service factory pointer.
+     * @brief The service factory pointer.
+     *
      * Note if the factory service is set, the svc field will not be used.
      *
      * The service factory will be called for every bundle requesting/de-requesting a service. This gives the provider the
@@ -141,13 +146,17 @@ typedef struct celix_service_registration_options {
     celix_service_factory_t *factory OPTS_INIT;
 
     /**
-     * The required service name. This is used to identify the service. A fully qualified name with a namespace is
+     * @brief The required service name.
+     *
+     * This is used to identify the service. A fully qualified name with a namespace is
      * advisable to prevent name collision. (e.g. EXAMPLE_PRESSURE_SENSOR).
      */
     const char *serviceName OPTS_INIT;
 
     /**
-     * The optional service properties. These contain meta information about the service in the
+     * @brief The optional service properties.
+     *
+     * These contain meta information about the service in the
      * form of string key/values. (e.g. the location of a pressure sensor: location=left-tire).
      *
      * When a service is registered the Celix framework will take ownership of the provided properties.
@@ -156,12 +165,15 @@ typedef struct celix_service_registration_options {
     celix_properties_t *properties OPTS_INIT;
 
     /**
-     * The optional service language. If this is NULL, CELIX_FRAMEWORK_SERVICE_LANGUAGE_C is used.
+     * @brief The optional service language.
+     *
+     * If this is NULL, CELIX_FRAMEWORK_SERVICE_LANGUAGE_C is used.
      */
     const char *serviceLanguage OPTS_INIT;
 
     /**
-     * The optional service version (in the form of <MAJOR>.<MINOR>.<MICRO>.<QUALIFIER>).
+     * @brief The optional service version (in the form of <MAJOR>.<MINOR>.<MICRO>.<QUALIFIER>).
+     *
      * If present consumer of the service can specific which service version range of
      * a specific service they are interested in. Note that it is the responsibility of the users to ensure that
      * service in those version range are compatible (binary of source). It is advisable to use semantic versioning
@@ -170,23 +182,25 @@ typedef struct celix_service_registration_options {
     const char *serviceVersion OPTS_INIT;
 
     /**
-     * Async data pointer for the async register callback.
+     * @brief Async data pointer for the async register callback.
      */
      void *asyncData OPTS_INIT;
 
     /**
-    * Async callback. Will be called after the a service is registered in the service registry using a async call.
-    * Will be called on the Celix event loop.
+     * @brief Async callback.
+     *
+     * Will be called after the a service is registered in the service registry using a async call.
+     * Will be called on the Celix event loop.
      *
      * If a asyns service registration is combined with a _sync_ service unregistration, it can happen that
      * unregistration happens before the registration event is processed. In this case the asyncCallback
      * will not be called.
-    */
+     */
     void (*asyncCallback)(void *data, long serviceId) OPTS_INIT;
 } celix_service_registration_options_t;
 
 /**
- * C Macro to create a empty celix_service_registration_options_t type.
+ * @brief C Macro to create a empty celix_service_registration_options_t type.
  */
 #ifndef __cplusplus
 #define CELIX_EMPTY_SERVICE_REGISTRATION_OPTIONS { .svc = NULL, \
@@ -200,7 +214,7 @@ typedef struct celix_service_registration_options {
 #endif
 
 /**
- * Register a service to the Celix framework using the provided service registration options.
+ * @brief Register a service to the Celix framework using the provided service registration options.
  *
  * The service will be registered async on the Celix event loop thread. This means that service registration is (probably)
  * not yet concluded when this function returns, but is added to the event loop..
@@ -214,7 +228,8 @@ typedef struct celix_service_registration_options {
 long celix_bundleContext_registerServiceWithOptionsAsync(celix_bundle_context_t *ctx, const celix_service_registration_options_t *opts);
 
 /**
- * Register a service to the Celix framework using the provided service registration options.
+ * @brief Register a service to the Celix framework using the provided service registration options.
+ *
  * Note: Please use the celix_bundleContext_registerServiceAsyncWithOptions instead.
  *
  * @param ctx The bundle context
@@ -224,14 +239,16 @@ long celix_bundleContext_registerServiceWithOptionsAsync(celix_bundle_context_t
 long celix_bundleContext_registerServiceWithOptions(celix_bundle_context_t *ctx, const celix_service_registration_options_t *opts); //__attribute__((deprecated("Use celix_bundleContext_registerServiceAsyncWithOptions instead!")));
 
 /**
- * Waits til the async service registration for the provided serviceId is done.
+ * @brief Waits til the async service registration for the provided serviceId is done.
+ *
  * Silently ignore service ids < 0.
  * Will directly return if there is no pending service registration for the provided service id.
  */
 void celix_bundleContext_waitForAsyncRegistration(celix_bundle_context_t* ctx, long serviceId);
 
 /**
- * Checks whether a service for the provided service id is registered in the service registry.
+ * @brief Checks whether a service for the provided service id is registered in the service registry.
+ *
  * Note return false if the service for the provided service id is still pending in the event loop.
  * Silently ignore service ids < 0 (returns false).
  *
@@ -241,7 +258,8 @@ bool celix_bundleContext_isServiceRegistered(celix_bundle_context_t* ctx, long s
 
 
 /**
- * Unregister the service or service factory with service id.
+ * @brief Unregister the service or service factory with service id.
+ *
  * The service will only be unregistered if the bundle of the bundle context is the owner of the service.
  *
  * Will log an error if service id is unknown. Will silently ignore services ids < 0.
@@ -253,7 +271,8 @@ void celix_bundleContext_unregisterService(celix_bundle_context_t *ctx, long ser
 
 
 /**
- * Unregister the service or service factory with service id.
+ * @brief Unregister the service or service factory with service id.
+ *
  * The service will only be unregistered if the bundle of the bundle context is the owner of the service.
  *
  * The service will be umregistered async on the Celix event loop thread. This means that service unregistration is (probably)
@@ -269,7 +288,8 @@ void celix_bundleContext_unregisterServiceAsync(celix_bundle_context_t *ctx, lon
 
 
 /**
- * Waits til the async service unregistration for the provided serviceId is done.
+ * @brief Waits til the async service unregistration for the provided serviceId is done.
+ *
  * Silently ignore service < 0.
  */
 void celix_bundleContext_waitForAsyncUnregistration(celix_bundle_context_t* ctx, long serviceId);
@@ -278,7 +298,7 @@ void celix_bundleContext_waitForAsyncUnregistration(celix_bundle_context_t* ctx,
 
 
 /**
- * Finds the highest ranking service and returns the service id.
+ * @brief Finds the highest ranking service and returns the service id.
  *
  * @param ctx The bundle context
  * @param serviceName The required service name
@@ -287,7 +307,7 @@ void celix_bundleContext_waitForAsyncUnregistration(celix_bundle_context_t* ctx,
 long celix_bundleContext_findService(celix_bundle_context_t *ctx, const char *serviceName);
 
 /**
- * Finds the services with the provided service name and returns a list of the found service ids.
+ * @brief Finds the services with the provided service name and returns a list of the found service ids.
  *
  * @param ctx The bundle context
  * @param serviceName The required service name
@@ -296,24 +316,28 @@ long celix_bundleContext_findService(celix_bundle_context_t *ctx, const char *se
 celix_array_list_t* celix_bundleContext_findServices(celix_bundle_context_t *ctx, const char *serviceName);
 
 /**
- * Service filter options which can be used to query for certain services.
+ * @brief Service filter options which can be used to query for certain services.
  */
 typedef struct celix_service_filter_options {
     /**
-     * The service name.
+     * @brief The service name.
+     *
      * If NULL is used any services which matches the filter string will be tracked.
      */
     const char* serviceName OPTS_INIT;
 
     /**
-     * The optional version range. If service are registered with a service version this attribute can be used to
+     * @brief The optional version range.
+     *
+     * If service are registered with a service version this attribute can be used to
      * only select service with a version in the version range.
      * It uses the maven version range format, e.g. [1.0.0,2.0.0) or [1.1.1], etc.
      */
     const char* versionRange OPTS_INIT;
 
     /**
-     * LDAP filter to use for fine tuning the filtering, e.g. (|(location=middle)(location=front))
+     * @brief LDAP filter to use for fine tuning the filtering, e.g. (|(location=middle)(location=front))
+     *
      * The filter will be applied to all the user provided and framework provided service properties.
      */
     const char* filter OPTS_INIT;
@@ -333,7 +357,7 @@ typedef struct celix_service_filter_options {
 } celix_service_filter_options_t;
 
 /**
- * C Macro to create a empty celix_service_filter_options_t type.
+ * @brief C Macro to create a empty celix_service_filter_options_t type.
  */
 #ifndef __cplusplus
 #define CELIX_EMPTY_SERVICE_FILTER_OPTIONS {.serviceName = NULL, .versionRange = NULL, .filter = NULL, .serviceLanguage = NULL, .ignoreServiceLanguage = false}
@@ -341,7 +365,7 @@ typedef struct celix_service_filter_options {
 
 
 /**
- * Finds the highest ranking service and returns the service id.
+ * @brief Finds the highest ranking service and returns the service id.
  *
  * @param ctx The bundle context
  * @param opts The pointer to the filter options.
@@ -350,7 +374,7 @@ typedef struct celix_service_filter_options {
 long celix_bundleContext_findServiceWithOptions(celix_bundle_context_t *ctx, const celix_service_filter_options_t *opts);
 
 /**
- * Finds the services conform the provider filter options and returns a list of the found service ids.
+ * @brief Finds the services conform the provider filter options and returns a list of the found service ids.
  *
  * @param ctx The bundle context
  * @param opts The pointer to the filter options.
@@ -359,7 +383,8 @@ long celix_bundleContext_findServiceWithOptions(celix_bundle_context_t *ctx, con
 celix_array_list_t* celix_bundleContext_findServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_filter_options_t *opts);
 
 /**
- * track the highest ranking service with the provided serviceName.
+ * @brief Track the highest ranking service with the provided serviceName.
+ *
  * The highest ranking services will used for the callback.
  * If a new and higher ranking services the callback with be called again with the new service.
  * If a service is removed a the callback with be called with next highest ranking service or NULL as service.
@@ -382,7 +407,8 @@ long celix_bundleContext_trackServiceAsync(
 );
 
 /**
- * track the highest ranking service with the provided serviceName.
+ * @brief Track the highest ranking service with the provided serviceName.
+ *
  * The highest ranking services will used for the callback.
  * If a new and higher ranking services the callback with be called again with the new service.
  * If a service is removed a the callback with be called with next highest ranking service or NULL as service.
@@ -403,7 +429,7 @@ long celix_bundleContext_trackService(
 ); //__attribute__((deprecated("Use celix_bundleContext_trackServiceSync instead!")));
 
 /**
- * track services with the provided serviceName.
+ * @brief Track services with the provided serviceName.
  *
  * The service tracker will be created async on the Celix event loop thread. This means that the function can return
  * before the tracker is created.
@@ -425,7 +451,8 @@ long celix_bundleContext_trackServicesAsync(
 );
 
 /**
- * track services with the provided serviceName.
+ * @brief Track services with the provided serviceName.
+ *
  * Note: Please use the celix_bundleContext_trackServicesAsync instead.
  *
  * @param ctx The bundle context.
@@ -445,21 +472,21 @@ long celix_bundleContext_trackServices(
 ); //__attribute__((deprecated("Use celix_bundleContext_trackServicesAsync instead!")));;
 
 /**
- * Service Tracker Options used to fine tune which services to track and the callback to be used for the tracked services.
+ * @brief Service Tracker Options used to fine tune which services to track and the callback to be used for the tracked services.
  */
 typedef struct celix_service_tracking_options {
     /**
-     * The service filter options, used to setup the filter for the service to track.
+     * @brief The service filter options, used to setup the filter for the service to track.
      */
     celix_service_filter_options_t filter OPTS_INIT;
 
     /**
-     * The optional callback pointer used in all the provided callback function (set, add, remove, setWithProperties, etc).
+     * @brief The optional callback pointer used in all the provided callback function (set, add, remove, setWithProperties, etc).
      */
     void* callbackHandle OPTS_INIT;
 
     /**
-     * The optional set callback will be called when a new highest ranking service is available conform the provided
+     * @brief The optional set callback will be called when a new highest ranking service is available conform the provided
      * service filter options.
      * @param handle The callbackHandle pointer as provided in the service tracker options.
      * @param svc The service pointer of the highest ranking service.
@@ -467,19 +494,19 @@ typedef struct celix_service_tracking_options {
     void (*set)(void *handle, void *svc) OPTS_INIT;
 
     /**
-     * The optional setWithProperties callback is handled as the set callback, but with the addition that the service properties
+     * @brief The optional setWithProperties callback is handled as the set callback, but with the addition that the service properties
      * will also be provided to the callback.
      */
     void (*setWithProperties)(void *handle, void *svc, const celix_properties_t *props) OPTS_INIT; //highest ranking
 
     /**
-     * The optional setWithOwner callback is handled as the set callback, but with the addition that the service properties
+     * @brief The optional setWithOwner callback is handled as the set callback, but with the addition that the service properties
      * and the bundle owning the service will also be provided to the callback.
      */
     void (*setWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *svcOwner) OPTS_INIT; //highest ranking
 
     /**
-     * The optional add callback will be called for every current and future service found conform the provided service filter
+     * @brief The optional add callback will be called for every current and future service found conform the provided service filter
      * options as long as the tracker is active.
      * @param handle The callbackHandle pointer as provided in the service tracker options.
      * @param svc The service pointer of a service matching the provided service filter options.
@@ -487,19 +514,19 @@ typedef struct celix_service_tracking_options {
     void (*add)(void *handle, void *svc) OPTS_INIT;
 
     /**
-     * The optional addWithProperties callback is handled as the add callback, but with the addition that the service properties
+     * @brief The optional addWithProperties callback is handled as the add callback, but with the addition that the service properties
      * will also be provided to the callback.
      */
     void (*addWithProperties)(void *handle, void *svc, const celix_properties_t *props) OPTS_INIT;
 
     /**
-     * The optional addWithOwner callback is handled as the add callback, but with the addition that the service properties
+     * @brief The optional addWithOwner callback is handled as the add callback, but with the addition that the service properties
      * and the bundle owning the service will also be provided to the callback.
      */
     void (*addWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *svcOwner) OPTS_INIT;
 
     /**
-     * The optional remove callback will be called for every service conform the provided service filter options that is
+     * @brief The optional remove callback will be called for every service conform the provided service filter options that is
      * unregistered. When the remove call is finished the removed services should be considered invalid. This means
      * that the callback provider should ensure that the removed service is not in use or going to be used after the
      * remove callback is finished.
@@ -510,25 +537,25 @@ typedef struct celix_service_tracking_options {
     void (*remove)(void *handle, void *svc) OPTS_INIT;
 
     /**
-     * The optional removeWithProperties callback is handled as the remove callback, but with the addition that the service properties
+     * @brief The optional removeWithProperties callback is handled as the remove callback, but with the addition that the service properties
      * will also be provided to the callback.
      */
     void (*removeWithProperties)(void *handle, void *svc, const celix_properties_t *props) OPTS_INIT;
 
     /**
-    * The optional removeWithOwner callback is handled as the remove callback, but with the addition that the service properties
+    * @brief The optional removeWithOwner callback is handled as the remove callback, but with the addition that the service properties
     * and the bundle owning the service will also be provided to the callback.
     */
     void (*removeWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *svcOwner) OPTS_INIT;
 
 
     /**
-     * Data for the trackerCreatedCallback.
+     * @brief Data for the trackerCreatedCallback.
      */
     void *trackerCreatedCallbackData OPTS_INIT;
 
     /**
-     * The callback called when the tracker has ben created (and is active) when using a async call.
+     * @brief The callback called when the tracker has ben created (and is active) when using a async call.
      *
      * If a asyns track service is combined with a _sync_ stop tracker, it can happen that
      * "stop tracker" happens before the "create tracker" event is processed. In this case the asyncCallback
@@ -538,7 +565,7 @@ typedef struct celix_service_tracking_options {
 } celix_service_tracking_options_t;
 
 /**
- * C Macro to create a empty celix_service_tracking_options_t type.
+ * @brief C Macro to create a empty celix_service_tracking_options_t type.
  */
 #ifndef __cplusplus
 #define CELIX_EMPTY_SERVICE_TRACKING_OPTIONS { .filter.serviceName = NULL, \
@@ -561,7 +588,8 @@ typedef struct celix_service_tracking_options {
 #endif
 
 /**
- * Tracks services using the provided tracker options.
+ * @brief Tracks services using the provided tracker options.
+ *
  * The tracker options are only using during this call and can safely be freed/reused after this call returns.
  *
  * The service tracker will be created async on the Celix event loop thread. This means that the function can return
@@ -574,7 +602,8 @@ typedef struct celix_service_tracking_options {
 long celix_bundleContext_trackServicesWithOptionsAsync(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts);
 
 /**
- * Tracks services using the provided tracker options.
+ * @brief Tracks services using the provided tracker options.
+ *
  * The tracker options are only using during this call and can safely be freed/reused after this call returns.
  * Note: Please use the celix_bundleContext_registerServiceFactoryAsync instead.
  *
@@ -586,7 +615,8 @@ long celix_bundleContext_trackServicesWithOptionsAsync(celix_bundle_context_t *c
 long celix_bundleContext_trackServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_tracking_options_t *opts); //__attribute__((deprecated("Use celix_bundleContext_trackServicesWithOptionsAsync instead!")));
 
 /**
- * Stop the tracker with the provided track id.
+ * @brief Stop the tracker with the provided track id.
+ *
  * Could be a service tracker, bundle tracker or service tracker tracker.
  * Only works for the trackers owned by the bundle of the bundle context.
  *
@@ -605,17 +635,18 @@ void celix_bundleContext_stopTrackerAsync(
         void (*doneCallback)(void* doneCallbackData));
 
 /**
- * Wait for (async) creation of tracker
+ * @brief Wait for (async) creation of tracker
  */
 void celix_bundleContext_waitForAsyncTracker(celix_bundle_context_t* ctx, long trackerId);
 
 /**
- * Wait for (async) stopping of tracking.
+ * @brief Wait for (async) stopping of tracking.
  */
 void celix_bundleContext_waitForAsyncStopTracker(celix_bundle_context_t* ctx, long trackerId);
 
 /**
- * Stop the tracker with the provided track id.
+ * @brief Stop the tracker with the provided track id.
+ *
  * Could be a service tracker, bundle tracker or service tracker tracker.
  * Only works for the trackers owned by the bundle of the bundle context.
  * Note: Please use the celix_bundleContext_registerServiceFactoryAsync instead.
@@ -627,7 +658,7 @@ void celix_bundleContext_stopTracker(celix_bundle_context_t *ctx, long trackerId
 
 
 /**
- * Use the service with the provided service id using the provided callback. The Celix framework will ensure that
+ * @brief Use the service with the provided service id using the provided callback. The Celix framework will ensure that
  * the targeted service cannot be removed during the callback.
  *
  * The svc is should only be considered valid during the callback.
@@ -652,8 +683,9 @@ bool celix_bundleContext_useServiceWithId(
 );
 
 /**
- * Use the highest ranking service with the provided service name using the provided callback. The Celix framework will
- * ensure that the targeted service cannot be removed during the callback.
+ * @brief Use the highest ranking service with the provided service name using the provided callback.
+ *
+ * The Celix framework will ensure that the targeted service cannot be removed during the callback.
  *
  * The svc is should only be considered valid during the callback.
  * If no service is found the callback will not be invoked.
@@ -675,8 +707,9 @@ bool celix_bundleContext_useService(
 );
 
 /**
- * Use the services with the provided service name using the provided callback. The Celix framework will
- * ensure that the targeted service cannot be removed during the callback.
+ * @brief Use the services with the provided service name using the provided callback.
+ *
+ * The Celix framework will ensure that the targeted service cannot be removed during the callback.
  *
  * The svc is should only be considered valid during the callback.
  * If no service is found the callback will not be invoked.
@@ -698,28 +731,28 @@ size_t celix_bundleContext_useServices(
 );
 
 /**
- * Service Use Options used to fine tune which services to use and which callbacks to use.
+ * @brief Service Use Options used to fine tune which services to use and which callbacks to use.
  */
 typedef struct celix_service_use_options {
     /**
-     * The service filter options, used to setup the filter for the service to track.
+     * @brief The service filter options, used to setup the filter for the service to track.
      */
     celix_service_filter_options_t filter OPTS_INIT;
 
     /**
-     * An optional timeout (in seconds), if > 0 the use service call will block until the timeout is expired or
+     * @brief An optional timeout (in seconds), if > 0 the use service call will block until the timeout is expired or
      * when at least one service is found.
      * Default (0)
      */
      double waitTimeoutInSeconds OPTS_INIT;
 
     /**
-     * The optional callback pointer used in all the provided callback function (set, add, remove, setWithProperties, etc).
+     * @brief The optional callback pointer used in all the provided callback function (set, add, remove, setWithProperties, etc).
      */
     void *callbackHandle OPTS_INIT;
 
     /**
-     * The optional use callback will be called when for every services found conform the service filter options
+     * @brief The optional use callback will be called when for every services found conform the service filter options
      * - in case of findServices - or only for the highest ranking service found - in case of findService -.
      *
      * @param handle The callbackHandle pointer as provided in the service tracker options.
@@ -728,20 +761,20 @@ typedef struct celix_service_use_options {
     void (*use)(void *handle, void *svc) OPTS_INIT;
 
     /**
-     * The optional useWithProperties callback is handled as the use callback, but with the addition that the service properties
+     * @brief The optional useWithProperties callback is handled as the use callback, but with the addition that the service properties
      * will also be provided to the callback.
      */
     void (*useWithProperties)(void *handle, void *svc, const celix_properties_t *props) OPTS_INIT;
 
     /**
-     * The optional useWithOwner callback is handled as the yse callback, but with the addition that the service properties
+     * @brief The optional useWithOwner callback is handled as the yse callback, but with the addition that the service properties
      * and the bundle owning the service will also be provided to the callback.
      */
     void (*useWithOwner)(void *handle, void *svc, const celix_properties_t *props, const celix_bundle_t *svcOwner) OPTS_INIT;
 } celix_service_use_options_t;
 
 /**
- * C Macro to create a empty celix_service_use_options_t type.
+ * @brief C Macro to create a empty celix_service_use_options_t type.
  */
 #ifndef __cplusplus
 #define CELIX_EMPTY_SERVICE_USE_OPTIONS {.filter.serviceName = NULL, \
@@ -756,8 +789,9 @@ typedef struct celix_service_use_options {
 #endif
 
 /**
- * Use the services with the provided service filter options using the provided callback. The Celix framework will
- * ensure that the targeted service cannot be removed during the callback.
+ * @brief Use the services with the provided service filter options using the provided callback.
+ *
+ * The Celix framework will ensure that the targeted service cannot be removed during the callback.
  *
  * The svc is should only be considered valid during the callback.
  * If no service is found the callback will not be invoked.
@@ -775,8 +809,9 @@ bool celix_bundleContext_useServiceWithOptions(
 
 
 /**
- * Use the services with the provided service filter options using the provided callback. The Celix framework will
- * ensure that the targeted service cannot be removed during the callback.
+ * @brief Use the services with the provided service filter options using the provided callback.
+ *
+ * The Celix framework will ensure that the targeted service cannot be removed during the callback.
  *
  * The svc is should only be considered valid during the callback.
  * If no service is found the callback will not be invoked.
@@ -796,7 +831,7 @@ size_t celix_bundleContext_useServicesWithOptions(
 
 
 /**
- * List the installed and started bundle ids.
+ * @brief List the installed and started bundle ids.
  * The bundle ids does not include the framework bundle (bundle id CELIX_FRAMEWORK_BUNDLE_ID).
  *
  * @param ctx The bundle context
@@ -805,7 +840,7 @@ size_t celix_bundleContext_useServicesWithOptions(
 celix_array_list_t* celix_bundleContext_listBundles(celix_bundle_context_t *ctx);
 
 /**
- * Check whether a bundle is installed.
+ * @brief Check whether a bundle is installed.
  * @param ctx       The bundle context.
  * @param bndId     The bundle id to check
  * @return          true if the bundle is installed.
@@ -813,7 +848,7 @@ celix_array_list_t* celix_bundleContext_listBundles(celix_bundle_context_t *ctx)
 bool celix_bundleContext_isBundleInstalled(celix_bundle_context_t *ctx, long bndId);
 
 /**
- * Check whether the bundle is active.
+ * @brief Check whether the bundle is active.
  * @param ctx       The bundle context.
  * @param bndId     The bundle id to check
  * @return          true if the bundle is installed and active.
@@ -822,7 +857,7 @@ bool celix_bundleContext_isBundleActive(celix_bundle_context_t *ctx, long bndId)
 
 
 /**
- * Install and optional start a bundle.
+ * @brief Install and optional start a bundle.
  * Will silently ignore bundle ids < 0.
  *
  * If this function is called on the Celix event thread and autoStart is true,
@@ -838,7 +873,7 @@ bool celix_bundleContext_isBundleActive(celix_bundle_context_t *ctx, long bndId)
 long celix_bundleContext_installBundle(celix_bundle_context_t *ctx, const char *bundleLoc, bool autoStart);
 
 /**
- * Uninstall the bundle with the provided bundle id. If needed the bundle will be stopped first.
+ * @brief Uninstall the bundle with the provided bundle id. If needed the bundle will be stopped first.
  * Will silently ignore bundle ids < 0.
  *
  * If this function is called on the Celix event thread, the actual stopping of the bundle will be done async and
@@ -853,7 +888,7 @@ long celix_bundleContext_installBundle(celix_bundle_context_t *ctx, const char *
 bool celix_bundleContext_uninstallBundle(celix_bundle_context_t *ctx, long bndId);
 
 /**
- * Stop the bundle with the provided bundle id.
+ * @brief Stop the bundle with the provided bundle id.
  * Will silently ignore bundle ids < 0.
  *
  * If this function is called on the Celix event thread, the actual stopping of the bundle will be done async and
@@ -868,7 +903,7 @@ bool celix_bundleContext_uninstallBundle(celix_bundle_context_t *ctx, long bndId
 bool celix_bundleContext_stopBundle(celix_bundle_context_t *ctx, long bndId);
 
 /**
- * Start the bundle with the provided bundle id.
+ * @brief Start the bundle with the provided bundle id.
  * Will silently ignore bundle ids < 0.
  *
  * If this function is called on the Celix event thread, the actual starting of the bundle will be done async and
@@ -883,7 +918,7 @@ bool celix_bundleContext_stopBundle(celix_bundle_context_t *ctx, long bndId);
 bool celix_bundleContext_startBundle(celix_bundle_context_t *ctx, long bndId);
 
 /**
- * Returns the bundle symbolic name for the provided bundle id.
+ * @brief Returns the bundle symbolic name for the provided bundle id.
  * The caller is owner of the return string.
  *
  * @param ctx The bundle context
@@ -894,7 +929,8 @@ char* celix_bundleContext_getBundleSymbolicName(celix_bundle_context_t *ctx, lon
 
 
 /**
- * track bundles
+ * @brief Track bundles.
+ *
  * The add bundle callback will also be called for already installed bundles.
  *
  * The bundle tracker will be created async on the Celix event loop thread. This means that the function can return
@@ -914,7 +950,8 @@ long celix_bundleContext_trackBundlesAsync(
 );
 
 /**
- * track bundles
+ * @brief Track bundles.
+ *
  * The add bundle callback will also be called for already installed bundles.
  *
  * Note: please use celix_bundleContext_trackBundlesAsync instead.
@@ -934,16 +971,16 @@ long celix_bundleContext_trackBundles(
 
 
 /**
- * The Service Bundle Tracking options can be used to fine tune the requested bundle tracker options.
+ * @brief The Service Bundle Tracking options can be used to fine tune the requested bundle tracker options.
  */
 typedef struct celix_bundle_tracker_options {
     /**
-     * The optional callback pointer used in all the provided callback function (set, add, remove, setWithProperties, etc).
+     * @brief The optional callback pointer used in all the provided callback function (set, add, remove, setWithProperties, etc).
      */
     void* callbackHandle OPTS_INIT;
 
     /**
-     * Tracker callback when a bundle is installed.
+     * @brief Tracker callback when a bundle is installed.
      * @param handle    The handle, contains the value of the callbackHandle.
      * @param bundle    The bundle which has been installed.
      *                  The bundle pointer is only guaranteed to be valid during the callback.
@@ -951,7 +988,7 @@ typedef struct celix_bundle_tracker_options {
     void (*onInstalled)(void *handle, const celix_bundle_t *bundle) OPTS_INIT;
 
     /**
-     * Tracker callback when a bundle is started.
+     * @brief Tracker callback when a bundle is started.
      * @param handle    The handle, contains the value of the callbackHandle.
      * @param bundle    The bundle which has been started.
      *                  The bundle pointer is only guaranteed to be valid during the callback.
@@ -959,7 +996,7 @@ typedef struct celix_bundle_tracker_options {
     void (*onStarted)(void *handle, const celix_bundle_t *bundle) OPTS_INIT;
 
     /**
-     * Tracker callback when a bundle is stopped.
+     * @brief Tracker callback when a bundle is stopped.
      * @param handle    The handle, contains the value of the callbackHandle.
      * @param bundle    The bundle which has been stopped.
      *                  The bundle pointer is only guaranteed to be valid during the callback.
@@ -974,18 +1011,18 @@ typedef struct celix_bundle_tracker_options {
     void (*onBundleEvent)(void *handle, const celix_bundle_event_t *event) OPTS_INIT;
 
     /**
-     * Default the framework bundle (bundle id 0) will not trigger the callbacks.
+     * @brief Default the framework bundle (bundle id 0) will not trigger the callbacks.
      * This is done, because the framework bundle is a special bundle which is generally not needed in the callbacks.
      */
     bool includeFrameworkBundle OPTS_INIT;
 
     /**
-     * Data for the trackerCreatedCallback.
+     * @brief Data for the trackerCreatedCallback.
      */
     void *trackerCreatedCallbackData OPTS_INIT;
 
     /**
-     * The callback called when the tracker has ben created (and is active) when using the
+     * @brief The callback called when the tracker has ben created (and is active) when using the
      * track bundles ascync calls.
      *
      * If a asyns track service is combined with a _sync_ stop tracker, it can happen that
@@ -996,14 +1033,15 @@ typedef struct celix_bundle_tracker_options {
 } celix_bundle_tracking_options_t;
 
 /**
- * C Macro to create a empty celix_service_filter_options_t type.
+ * @brief C Macro to create a empty celix_service_filter_options_t type.
  */
 #ifndef __cplusplus
 #define CELIX_EMPTY_BUNDLE_TRACKING_OPTIONS {.callbackHandle = NULL, .onInstalled = NULL, .onStarted = NULL, .onStopped = NULL, .onBundleEvent = NULL, .includeFrameworkBundle = false, .trackerCreatedCallbackData = NULL, .trackerCreatedCallback = NULL}
 #endif
 
 /**
- * Tracks bundles using the provided bundle tracker options.
+ * @brief Tracks bundles using the provided bundle tracker options.
+ *
  * The tracker options are only using during this call and can safely be freed/reused after this call returns.
  * (i.e. can be on the stack)
  *
@@ -1020,7 +1058,8 @@ long celix_bundleContext_trackBundlesWithOptionsAsync(
 );
 
 /**
- * Tracks bundles using the provided bundle tracker options.
+ * @brief Tracks bundles using the provided bundle tracker options.
+ *
  * The tracker options are only using during this call and can safely be freed/reused after this call returns.
  * (i.e. can be on the stack)
  *
@@ -1036,7 +1075,8 @@ long celix_bundleContext_trackBundlesWithOptions(
 ); //__attribute__((deprecated("Use celix_bundleContext_trackBundlesWithOptionsAsync instead!")));
 
 /**
- * Use the bundle with the provided bundle id if it is in the active (started) state
+ * @brief Use the bundle with the provided bundle id if it is in the active (started) state.
+ *
  * The provided callback will be called if the bundle is found and in the active (started) state.
  *
  * @param ctx               The bundle context.
@@ -1054,7 +1094,8 @@ bool celix_bundleContext_useBundle(
 );
 
 /**
- * Use the currently active (started) bundles.
+ * @brief Use the currently active (started) bundles.
+ *
  * The provided callback will be called for all the currently started bundles (excluding the framework bundle).
  *
  * @param ctx               The bundle context.
@@ -1068,22 +1109,17 @@ void celix_bundleContext_useBundles(
         void (*use)(void *handle, const celix_bundle_t *bundle)
 );
 
-
-
-//TODO add useBundleWithOptions (e.g. which state)
-//TODO findBundles
-
 /**
- * Service Tracker Info provided to the service tracker tracker callbacks.
+ * @brief Service Tracker Info provided to the service tracker tracker callbacks.
  */
 typedef struct celix_service_tracker_info {
     /**
-     * The parsed service filter, e.g. parsed "(&(objectClass=example_calc)(service.language=C)(meta.info=foo))"
+     * @brief The parsed service filter, e.g. parsed "(&(objectClass=example_calc)(service.language=C)(meta.info=foo))"
      */
     celix_filter_t *filter;
 
     /**
-     *The service name filter attribute parsed from the service filter (i.e. the value of the objectClass attribute key)
+     * @brief The service name filter attribute parsed from the service filter (i.e. the value of the objectClass attribute key)
      */
     const char *serviceName;
 
@@ -1094,14 +1130,15 @@ typedef struct celix_service_tracker_info {
     const char *serviceLanguage;
 
     /**
-     * Bundle id of the owner of the service tracker.
+     * @brief Bundle id of the owner of the service tracker.
      */
     long bundleId;
 } celix_service_tracker_info_t;
 
 /**
- * Track the service tracker targeting the provided service name. This can be used to track if there is an interest
- * in a certain service and ad-hoc act on that interest.
+ * @brief Track the service tracker targeting the provided service name.
+ *
+ * This can be used to track if there is an interest in a certain service and ad-hoc act on that interest.
  *
  * Note that the celix_service_tracker_info_t pointer in the trackerAdd/trackerRemove callbacks are only valid during
  * the callback.
@@ -1135,8 +1172,9 @@ long celix_bundleContext_trackServiceTrackersAsync(
         void (*doneCallback)(void* doneCallbackData));
 
 /**
- * Track the service tracker targeting the provided service name. This can be used to track if there is an interest
- * in a certain service and ad-hoc act on that interest.
+ * @brief Track the service tracker targeting the provided service name.
+ *
+ * This can be used to track if there is an interest in a certain service and ad-hoc act on that interest.
  *
  * Note that the celix_service_tracker_info_t pointer in the trackerAdd/trackerRemove callbacks are only valid during
  * the callback.
@@ -1162,7 +1200,7 @@ long celix_bundleContext_trackServiceTrackers(
         void (*trackerRemove)(void *handle, const celix_service_tracker_info_t *info)); //__attribute__((deprecated("Use celix_bundleContext_trackServiceTrackersAsync instead!")));
 
 /**
- * Gets the dependency manager for this bundle context.
+ * @brief Gets the dependency manager for this bundle context.
  *
  * @return the dependency manager or NULL if unsuccessful.
  */
@@ -1170,26 +1208,26 @@ celix_dependency_manager_t* celix_bundleContext_getDependencyManager(celix_bundl
 
 
 /**
- * Wait until all Celix event for this bundle are completed.
+ * @brief Wait until all Celix event for this bundle are completed.
  */
 void celix_bundleContext_waitForEvents(celix_bundle_context_t* ctx);
 
 
 /**
- * Returns the bundle for this bundle context.
+ * @brief Returns the bundle for this bundle context.
  */
 celix_bundle_t* celix_bundleContext_getBundle(const celix_bundle_context_t *ctx);
 
 
 /**
- * Returns the bundle if for the bundle of this bundle context.
+ * @brief Returns the bundle if for the bundle of this bundle context.
  */
 long celix_bundleContext_getBundleId(const celix_bundle_context_t *ctx);
 
 celix_framework_t* celix_bundleContext_getFramework(const celix_bundle_context_t* ctx);
 
 /**
- * Logs a message to Celix framework logger with the provided log level.
+ * @brief Logs a message to Celix framework logger with the provided log level.
  * @param ctx       The bundle context
  * @param level     The log level to use
  * @param format    printf style format string
@@ -1198,13 +1236,13 @@ celix_framework_t* celix_bundleContext_getFramework(const celix_bundle_context_t
 void celix_bundleContext_log(const celix_bundle_context_t* ctx, celix_log_level_e level, const char* format, ...);
 
 /**
- * Logs a message to Celix framework logger with the provided log level.
+ * @brief Logs a message to Celix framework logger with the provided log level.
  */
 void celix_bundleContext_vlog(const celix_bundle_context_t* ctx, celix_log_level_e level, const char* format, va_list formatArgs);
 
 
 /**
- * Gets the config property - or environment variable if the config property does not exist - for the provided name.
+ * @brief Gets the config property - or environment variable if the config property does not exist - for the provided name.
  * @param key The key of the property to receive.
  * @param defaultVal The default value to use if the property is not found (can be NULL).
  * @return The property value for the provided key or the provided defaultValue is the key is not found.
@@ -1212,7 +1250,7 @@ void celix_bundleContext_vlog(const celix_bundle_context_t* ctx, celix_log_level
 const char* celix_bundleContext_getProperty(celix_bundle_context_t *ctx, const char *key, const char *defaultVal);
 
 /**
- * Gets the config property as converts it to long. If the property is not a valid long, the defaultValue will be returned.
+ * @brief Gets the config property as converts it to long. If the property is not a valid long, the defaultValue will be returned.
  * The rest of the behaviour is the same as celix_bundleContext_getProperty.
 
  * @param key The key of the property to receive.
@@ -1222,7 +1260,7 @@ const char* celix_bundleContext_getProperty(celix_bundle_context_t *ctx, const c
 long celix_bundleContext_getPropertyAsLong(celix_bundle_context_t *ctx, const char *key, long defaultValue);
 
 /**
- * Gets the config property as converts it to double. If the property is not a valid double, the defaultValue will be returned.
+ * @brief Gets the config property as converts it to double. If the property is not a valid double, the defaultValue will be returned.
  * The rest of the behaviour is the same as celix_bundleContext_getProperty.
 
  * @param key The key of the property to receive.
@@ -1232,7 +1270,7 @@ long celix_bundleContext_getPropertyAsLong(celix_bundle_context_t *ctx, const ch
 double celix_bundleContext_getPropertyAsDouble(celix_bundle_context_t *ctx, const char *key, double defaultValue);
 
 /**
- * Gets the config property as converts it to bool. If the property is not a valid bool, the defaultValue will be returned.
+ * @brief Gets the config property as converts it to bool. If the property is not a valid bool, the defaultValue will be returned.
  * The rest of the behaviour is the same as celix_bundleContext_getProperty.
 
  * @param key The key of the property to receive.
diff --git a/libs/framework/include/celix_constants.h b/libs/framework/include/celix_constants.h
index b013317..ec4a405 100644
--- a/libs/framework/include/celix_constants.h
+++ b/libs/framework/include/celix_constants.h
@@ -144,6 +144,9 @@ extern "C" {
 #define OSGI_FRAMEWORK_DEPRECATED_BUNDLE_ACTIVATOR_DESTROY "bundleActivator_destroy"
 
 #define CELIX_FRAMEWORK_BUNDLE_SYMBOLICNAME "Bundle-SymbolicName"
+#define CELIX_FRAMEWORK_BUNDLE_NAME "Bundle-Name"
+#define CELIX_FRAMEWORK_BUNDLE_GROUP "Bundle-Group"
+#define CELIX_FRAMEWORK_BUNDLE_DESCRIPTION "Bundle-Description"
 #define CELIX_FRAMEWORK_BUNDLE_VERSION "Bundle-Version"
 #define CELIX_FRAMEWORK_PRIVATE_LIBRARY "Private-Library"
 #define CELIX_FRAMEWORK_EXPORT_LIBRARY "Export-Library"
@@ -270,6 +273,15 @@ extern "C" {
  */
 #define CELIX_AUTO_START_6 "CELIX_AUTO_START_6"
 
+/**
+ * @brief Celix framework environment property (named "CELIX_FRAMEWORK_WARN_FOR_MISSING_SERVICE_VERSION")
+ * which configures the Celix framework to print warning when service are registered without a version property.
+ *
+ * Default is true.
+ */
+#define CELIX_FRAMEWORK_WARN_FOR_MISSING_SERVICE_VERSION "CELIX_FRAMEWORK_WARN_FOR_MISSING_SERVICE_VERSION"
+
+#define CELIX_FRAMEWORK_WARN_FOR_MISSING_SERVICE_VERSION_DEFAULT    false
 
 #ifdef __cplusplus
 }
diff --git a/libs/framework/include/module.h b/libs/framework/include/module.h
index c37b2d3..d81ca87 100644
--- a/libs/framework/include/module.h
+++ b/libs/framework/include/module.h
@@ -89,6 +89,10 @@ FRAMEWORK_EXPORT celix_array_list_t *module_getDependents(module_pt module);
 
 FRAMEWORK_EXPORT celix_status_t module_getGroup(module_pt module, const char **group);
 
+FRAMEWORK_EXPORT celix_status_t module_getName(module_pt module, const char **name);
+
+FRAMEWORK_EXPORT celix_status_t module_getDescription(module_pt module, const char **descriptoin);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/framework/src/bundle.c b/libs/framework/src/bundle.c
index 4dd20be..97cbe71 100644
--- a/libs/framework/src/bundle.c
+++ b/libs/framework/src/bundle.c
@@ -32,6 +32,7 @@
 #include "bundle_context_private.h"
 #include "service_tracker_private.h"
 
+
 celix_status_t bundle_createModule(bundle_pt bundle, module_pt *module);
 celix_status_t bundle_closeRevisions(const_bundle_pt bundle);
 
@@ -107,6 +108,9 @@ celix_status_t bundle_destroy(bundle_pt bundle) {
 	arrayList_destroy(bundle->modules);
 
 	free(bundle->symbolicName);
+    free(bundle->name);
+    free(bundle->group);
+    free(bundle->description);
 	free(bundle);
 
 	return CELIX_SUCCESS;
@@ -357,9 +361,18 @@ celix_status_t bundle_addModule(bundle_pt bundle, module_pt module) {
 	resolver_addModule(module);
 	if (bundle->symbolicName == NULL) {
 		const char *sn = NULL;
+        const char *n = NULL;
+        const char *g = NULL;
+        const char *d = NULL;
 		module_getSymbolicName(module, &sn);
+        module_getGroup(module, &g);
+        module_getName(module, &n);
+        module_getDescription(module, &d);
 		if (sn != NULL) {
             bundle->symbolicName = celix_utils_strdup(sn);
+            bundle->name = celix_utils_strdup(n);
+            bundle->group = celix_utils_strdup(g);
+            bundle->description = celix_utils_strdup(d);
         }
 	}
 
@@ -577,21 +590,21 @@ char* celix_bundle_getEntry(const bundle_t* bnd, const char *path) {
 
 
 const char* celix_bundle_getGroup(const celix_bundle_t *bnd) {
-	const char *result = NULL;
-	if (bnd != NULL) {
-		module_pt mod = NULL;
-		bundle_getCurrentModule((celix_bundle_t*)bnd, &mod);
-		if (mod != NULL) {
-			module_getGroup(mod, &result);
-		}
-	}
-	return result;
+	return bnd->group;
 }
 
 const char* celix_bundle_getSymbolicName(const celix_bundle_t *bnd) {
 	return bnd->symbolicName;
 }
 
+const char* celix_bundle_getName(const celix_bundle_t* bnd) {
+    return bnd->name;
+}
+
+const char* celix_bundle_getDescription(const celix_bundle_t* bnd) {
+    return bnd->description;
+}
+
 bool celix_bundle_isSystemBundle(const celix_bundle_t *bnd) {
     return bnd != NULL && celix_bundle_getId(bnd) == 0;
 }
diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c
index ae9b68f..ddc2c94 100644
--- a/libs/framework/src/bundle_context.c
+++ b/libs/framework/src/bundle_context.c
@@ -39,6 +39,7 @@
 #include "module.h"
 #include "service_tracker_private.h"
 #include "celix_array_list.h"
+#include "celix_libloader.h"
 
 static celix_status_t bundleContext_bundleChanged(void *handle, bundle_event_t *event);
 static void bundleContext_cleanupBundleTrackers(bundle_context_t *ct);
diff --git a/libs/framework/src/bundle_private.h b/libs/framework/src/bundle_private.h
index 633fcdd..cff093e 100644
--- a/libs/framework/src/bundle_private.h
+++ b/libs/framework/src/bundle_private.h
@@ -27,7 +27,10 @@
 
 struct celix_bundle {
 	bundle_context_pt context;
-    char *symbolicName; //for debug
+    char *symbolicName;
+	char *name;
+	char *group;
+	char *description;
 	struct celix_bundle_activator *activator;
 	bundle_state_e state;
 	void * handle;
diff --git a/libs/framework/src/celix_library_loader.c b/libs/framework/src/celix_libloader.c
similarity index 77%
rename from libs/framework/src/celix_library_loader.c
rename to libs/framework/src/celix_libloader.c
index 9889065..c7d855d 100644
--- a/libs/framework/src/celix_library_loader.c
+++ b/libs/framework/src/celix_libloader.c
@@ -17,31 +17,36 @@
  * under the License.
  */
 
-#include "celix_constants.h"
-#include "celix_library_loader.h"
 #include <dlfcn.h>
 
+#include "celix_constants.h"
+#include "celix_libloader.h"
+#include "utils.h"
+
 celix_library_handle_t* celix_libloader_open(celix_bundle_context_t *ctx, const char *libPath) {
-#if defined(DEBUG) && !defined(ANDROID)
-    bool def = true;
-#else
-    bool def = false;
+    bool defaultNoDelete = true;
+#if defined(NDEBUG)
+    bool defaultNoDelete = false;
 #endif
-    bool noDelete = celix_bundleContext_getPropertyAsBool(ctx, CELIX_LOAD_BUNDLES_WITH_NODELETE, def);
+    celix_library_handle_t* handle = NULL;
+    bool noDelete = celix_bundleContext_getPropertyAsBool(ctx, CELIX_LOAD_BUNDLES_WITH_NODELETE, defaultNoDelete);
+    int flags = RTLD_LAZY|RTLD_LOCAL;
     if (noDelete) {
-        return dlopen(libPath, RTLD_LAZY|RTLD_LOCAL|RTLD_NODELETE);
-    } else {
-        return dlopen(libPath, RTLD_LAZY|RTLD_LOCAL);
+        flags = RTLD_LAZY|RTLD_LOCAL|RTLD_NODELETE;
     }
+    handle = dlopen(libPath, flags);
+    return handle;
 }
 
 
 void celix_libloader_close(celix_library_handle_t *handle) {
     dlclose(handle);
 }
+
 void* celix_libloader_getSymbol(celix_library_handle_t *handle, const char *name) {
     return dlsym(handle, name);
 }
+
 const char* celix_libloader_getLastError() {
     return dlerror();
 }
\ No newline at end of file
diff --git a/libs/framework/src/celix_library_loader.h b/libs/framework/src/celix_libloader.h
similarity index 54%
rename from libs/framework/src/celix_library_loader.h
rename to libs/framework/src/celix_libloader.h
index f0779d1..2691b62 100644
--- a/libs/framework/src/celix_library_loader.h
+++ b/libs/framework/src/celix_libloader.h
@@ -17,16 +17,44 @@
  * under the License.
  */
 
-#ifndef CELIX_CELIX_LIBRARY_LOADER_H
-#define CELIX_CELIX_LIBRARY_LOADER_H
+#ifndef CELIX_CELIX_LIBLOADER_H
+#define CELIX_CELIX_LIBLOADER_H
 
 #include "celix_bundle_context.h"
 
 typedef void celix_library_handle_t;
 
+/**
+ * @brief Load library using the provided path.
+ * @return a library handle or NULL if the library could not be loaded.
+ */
 celix_library_handle_t* celix_libloader_open(celix_bundle_context_t *ctx, const char *libPath);
+
+/**
+ * @brief Close the library
+ */
 void celix_libloader_close(celix_library_handle_t *handle);
+
+/**
+ * @brief Get the address of a symbol with the provided name.
+ * @return The symbol address of NULL if the symbol could not be found.
+ */
 void* celix_libloader_getSymbol(celix_library_handle_t *handle, const char *name);
+
+/**
+ * @brief Returns the last celix_libloader_open error
+ * @return The last error or NULL.
+ */
 const char* celix_libloader_getLastError();
 
-#endif //CELIX_CELIX_LIBRARY_LOADER_H
+/**
+ * @brief Tries to find the bundle activator library for the provided addr and if found tries to
+ * find the symbol address for the provided symbol name in the bundle activator library.
+ *
+ * @param addr A address which is use to lookup the bundle activator library (using dladdr1)
+ * @param name The symbol name to find
+ * @return The found symbol address or NULL.
+ */
+void* celix_libloader_findBundleActivatorSymbolFromAddr(void *addr, const char* name);
+
+#endif //CELIX_CELIX_LIBLOADER_H
diff --git a/libs/framework/src/framework.c b/libs/framework/src/framework.c
index e10d259..9b5caa1 100644
--- a/libs/framework/src/framework.c
+++ b/libs/framework/src/framework.c
@@ -37,8 +37,7 @@
 #include "bundle_private.h"
 #include "celix_bundle_context.h"
 #include "bundle_context_private.h"
-#include "service_tracker.h"
-#include "celix_library_loader.h"
+#include "celix_libloader.h"
 #include "celix_log_constants.h"
 
 typedef celix_status_t (*create_function_fp)(bundle_context_t *context, void **userData);
@@ -259,7 +258,7 @@ celix_status_t framework_create(framework_pt *out, celix_properties_t* config) {
     }
     framework->logger = celix_frameworkLogger_create(celix_logUtils_logLevelFromString(logStr, CELIX_LOG_LEVEL_INFO));
 
-    celix_status_t status =bundle_create(&framework->bundle);
+    celix_status_t status = bundle_create(&framework->bundle);
     status = CELIX_DO_IF(status, bundle_getBundleId(framework->bundle, &framework->bundleId));
     status = CELIX_DO_IF(status, bundle_setFramework(framework->bundle, framework));
     status = CELIX_DO_IF(status, bundleCache_create(uuid, framework->configurationMap, &framework->cache));
diff --git a/libs/framework/src/manifest_parser.c b/libs/framework/src/manifest_parser.c
index 13a2b2e..038af09 100644
--- a/libs/framework/src/manifest_parser.c
+++ b/libs/framework/src/manifest_parser.c
@@ -45,7 +45,6 @@ struct manifestParser {
 	manifest_pt manifest;
 
 	version_pt bundleVersion;
-	char * bundleSymbolicName;
 	linked_list_pt capabilities;
 	linked_list_pt requirements;
 };
@@ -64,7 +63,6 @@ celix_status_t manifestParser_create(module_pt owner, manifest_pt manifest, mani
 	parser = (manifest_parser_pt) malloc(sizeof(*parser));
 	if (parser) {
 		const char * bundleVersion = NULL;
-		const char * bundleSymbolicName = NULL;
 		parser->manifest = manifest;
 		parser->owner = owner;
 
@@ -76,10 +74,6 @@ celix_status_t manifestParser_create(module_pt owner, manifest_pt manifest, mani
 			parser->bundleVersion = NULL;
 			version_createEmptyVersion(&parser->bundleVersion);
 		}
-		bundleSymbolicName = manifest_getValue(manifest, OSGI_FRAMEWORK_BUNDLE_SYMBOLICNAME);
-		if (bundleSymbolicName != NULL) {
-			parser->bundleSymbolicName = (char*)bundleSymbolicName;
-		}
 
 		parser->capabilities = manifestParser_parseExportHeader(owner, manifest_getValue(manifest, OSGI_FRAMEWORK_EXPORT_LIBRARY));
 		parser->requirements = manifestParser_parseImportHeader(manifest_getValue(manifest, OSGI_FRAMEWORK_IMPORT_LIBRARY));
@@ -101,7 +95,6 @@ celix_status_t manifestParser_destroy(manifest_parser_pt mp) {
 	mp->capabilities = NULL;
 	linkedList_destroy(mp->requirements);
 	mp->requirements = NULL;
-	mp->bundleSymbolicName = NULL;
 	version_destroy(mp->bundleVersion);
 	mp->bundleVersion = NULL;
 	mp->manifest = NULL;
@@ -465,19 +458,30 @@ static linked_list_pt manifestParser_parseExportHeader(module_pt module, const c
 	return capabilities;
 }
 
+static celix_status_t manifestParser_getDuplicateEntry(manifest_parser_pt parser, const char* entryName, char **result) {
+    const char *val = manifest_getValue(parser->manifest, entryName);
+    if (result != NULL && val == NULL) {
+        *result = NULL;
+    } else if (result != NULL) {
+        *result = celix_utils_strdup(val);
+    }
+    return CELIX_SUCCESS;
+}
+
 celix_status_t manifestParser_getAndDuplicateGroup(manifest_parser_pt parser, char **group) {
-	const char *val = manifest_getValue(parser->manifest, "Bundle-Group");
-	if (group != NULL && val == NULL) {
-		*group = NULL;
-	} else if (group != NULL) {
-		*group = strndup(val, 1024*10);
-	}
-	return CELIX_SUCCESS;
+    return manifestParser_getDuplicateEntry(parser, CELIX_FRAMEWORK_BUNDLE_GROUP, group);
 }
 
 celix_status_t manifestParser_getAndDuplicateSymbolicName(manifest_parser_pt parser, char **symbolicName) {
-	*symbolicName = strndup(parser->bundleSymbolicName, 1024*10);
-	return CELIX_SUCCESS;
+    return manifestParser_getDuplicateEntry(parser, OSGI_FRAMEWORK_BUNDLE_SYMBOLICNAME, symbolicName);
+}
+
+celix_status_t manifestParser_getAndDuplicateName(manifest_parser_pt parser, char **name) {
+    return manifestParser_getDuplicateEntry(parser, CELIX_FRAMEWORK_BUNDLE_NAME, name);
+}
+
+celix_status_t manifestParser_getAndDuplicateDescription(manifest_parser_pt parser, char **description) {
+    return manifestParser_getDuplicateEntry(parser, CELIX_FRAMEWORK_BUNDLE_DESCRIPTION, description);
 }
 
 celix_status_t manifestParser_getBundleVersion(manifest_parser_pt parser, version_pt *version) {
diff --git a/libs/framework/src/manifest_parser.h b/libs/framework/src/manifest_parser.h
index a5d7063..0acae27 100644
--- a/libs/framework/src/manifest_parser.h
+++ b/libs/framework/src/manifest_parser.h
@@ -38,6 +38,8 @@ celix_status_t manifestParser_create(module_pt owner, manifest_pt manifest, mani
 celix_status_t manifestParser_destroy(manifest_parser_pt mp);
 
 celix_status_t manifestParser_getAndDuplicateSymbolicName(manifest_parser_pt parser, char **symbolicName);
+celix_status_t manifestParser_getAndDuplicateName(manifest_parser_pt parser, char **name);
+celix_status_t manifestParser_getAndDuplicateDescription(manifest_parser_pt parser, char **description);
 celix_status_t manifestParser_getBundleVersion(manifest_parser_pt parser, version_pt *version);
 celix_status_t manifestParser_getAndDuplicateGroup(manifest_parser_pt parser, char **group);
 celix_status_t manifestParser_getCapabilities(manifest_parser_pt parser, linked_list_pt *capabilities);
diff --git a/libs/framework/src/module.c b/libs/framework/src/module.c
index f02f907..1a1be97 100644
--- a/libs/framework/src/module.c
+++ b/libs/framework/src/module.c
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <celix_utils.h>
 
 #include "module.h"
 #include "manifest_parser.h"
@@ -33,8 +34,10 @@ struct module {
 	array_list_pt dependentImporters;
 
 	version_pt version;
-	char * symbolicName;
-	char * group;
+	char* symbolicName;
+	char* group;
+    char* name;
+    char* description;
 	bool resolved;
 
 	manifest_pt headerMap;
@@ -64,6 +67,12 @@ module_pt module_create(manifest_pt headerMap, const char * moduleId, bundle_pt
             module->group = NULL;
             manifestParser_getAndDuplicateGroup(mp, &module->group);
 
+            module->name = NULL;
+            manifestParser_getAndDuplicateName(mp, &module->name);
+
+            module->description = NULL;
+            manifestParser_getAndDuplicateDescription(mp, &module->description);
+
             module->version = NULL;
             manifestParser_getBundleVersion(mp, &module->version);
 
@@ -88,8 +97,10 @@ module_pt module_createFrameworkModule(bundle_pt bundle) {
 	module = (module_pt) calloc(1, sizeof(*module));
 	if (module) {
         module->id = strdup("0");
-        module->symbolicName = strdup("Framework");
-        module->group = strdup("Celix/Framework");
+        module->symbolicName = celix_utils_strdup("celix_framework");
+        module->group = celix_utils_strdup("Celix/Framework");
+        module->name = celix_utils_strdup("Celix Framework");
+        module->description = celix_utils_strdup("The Celix Framework System Bundle");
         module->version = NULL;
         version_createVersion(1, 0, 0, "", &module->version);
 
@@ -147,7 +158,9 @@ void module_destroy(module_pt module) {
 
 	free(module->id);
 	free(module->symbolicName);
-	free(module->group);
+    free(module->name);
+    free(module->group);
+    free(module->description);
 	free(module);
 }
 
@@ -176,16 +189,44 @@ version_pt module_getVersion(module_pt module) {
 
 celix_status_t module_getSymbolicName(module_pt module, const char **symbolicName) {
 	celix_status_t status = CELIX_SUCCESS;
-
 	if (module == NULL) {
 		status = CELIX_ILLEGAL_ARGUMENT;
 	} else {
 		*symbolicName = module->symbolicName;
 	}
-
 	return status;
 }
 
+celix_status_t module_getName(module_pt module, const char **symbolicName) {
+    celix_status_t status = CELIX_SUCCESS;
+    if (module == NULL) {
+        status = CELIX_ILLEGAL_ARGUMENT;
+    } else {
+        *symbolicName = module->name;
+    }
+    return status;
+}
+
+celix_status_t module_getGroup(module_pt module, const char **symbolicName) {
+    celix_status_t status = CELIX_SUCCESS;
+    if (module == NULL) {
+        status = CELIX_ILLEGAL_ARGUMENT;
+    } else {
+        *symbolicName = module->group;
+    }
+    return status;
+}
+
+celix_status_t module_getDescription(module_pt module, const char **symbolicName) {
+    celix_status_t status = CELIX_SUCCESS;
+    if (module == NULL) {
+        status = CELIX_ILLEGAL_ARGUMENT;
+    } else {
+        *symbolicName = module->description;
+    }
+    return status;
+}
+
 char * module_getId(module_pt module) {
 	return module->id;
 }
@@ -278,11 +319,4 @@ array_list_pt module_getDependents(module_pt module) {
     arrayList_addAll(dependents, module->dependentImporters);
 
     return dependents;
-}
-
-celix_status_t module_getGroup(module_pt module, const char **group) {
-    if (group != NULL) {
-        *group = module->group;
-    }
-    return CELIX_SUCCESS;
 }
\ No newline at end of file
diff --git a/libs/framework/src/service_registry.c b/libs/framework/src/service_registry.c
index 876965c..626dc57 100644
--- a/libs/framework/src/service_registry.c
+++ b/libs/framework/src/service_registry.c
@@ -191,10 +191,9 @@ static celix_status_t serviceRegistry_registerServiceInternal(service_registry_p
 	array_list_pt regs;
 	long svcId = reservedId > 0 ? reservedId : celix_serviceRegistry_nextSvcId(registry);
 
-	celix_properties_setLong(dictionary, CELIX_FRAMEWORK_SERVICE_BUNDLE_ID, celix_bundle_getId(bundle));
+    celix_properties_setLong(dictionary, CELIX_FRAMEWORK_SERVICE_BUNDLE_ID, celix_bundle_getId(bundle));
 
-
-	if (svcType == CELIX_DEPRECATED_FACTORY_SERVICE) {
+    if (svcType == CELIX_DEPRECATED_FACTORY_SERVICE) {
 	    celix_properties_set(dictionary, CELIX_FRAMEWORK_SERVICE_SCOPE, CELIX_FRAMEWORK_SERVICE_SCOPE_BUNDLE);
         *registration = serviceRegistration_createServiceFactory(registry->callback, bundle, serviceName,
                                                                  svcId, serviceObject,
diff --git a/libs/framework/src/service_tracker.c b/libs/framework/src/service_tracker.c
index b8e4b0c..344052b 100644
--- a/libs/framework/src/service_tracker.c
+++ b/libs/framework/src/service_tracker.c
@@ -49,6 +49,8 @@ static inline celix_tracked_entry_t* tracked_create(service_reference_pt ref, vo
     celix_tracked_entry_t *tracked = calloc(1, sizeof(*tracked));
     tracked->reference = ref;
     tracked->service = svc;
+    tracked->serviceId = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_ID, -1);
+    tracked->serviceRanking = celix_properties_getAsLong(props, CELIX_FRAMEWORK_SERVICE_RANKING, 0);
     tracked->properties = props;
     tracked->serviceOwner = bnd;
     tracked->serviceName = celix_properties_get(props, OSGI_FRAMEWORK_OBJECTCLASS, "Error");
@@ -437,7 +439,10 @@ static celix_status_t serviceTracker_track(service_tracker_t* tracker, service_r
             arrayList_add(tracker->trackedServices, tracked);
             celixThreadMutex_unlock(&tracker->mutex);
 
-            celix_serviceTracker_useHighestRankingService(tracker, tracked->serviceName, tracker, NULL, NULL, serviceTracker_checkAndInvokeSetService);
+            if (tracker->set != NULL || tracker->setWithProperties != NULL || tracker->setWithOwner != NULL) {
+                celix_serviceTracker_useHighestRankingService(tracker, tracked->serviceName, tracker, NULL, NULL,
+                                                              serviceTracker_checkAndInvokeSetService);
+            }
             serviceTracker_invokeAddService(tracker, tracked);
         }
     } else {
@@ -718,7 +723,6 @@ bool celix_serviceTracker_useHighestRankingService(service_tracker_t *tracker,
     bool called = false;
     celix_tracked_entry_t *tracked = NULL;
     celix_tracked_entry_t *highest = NULL;
-    long highestRank = 0;
     unsigned int i;
 
     //first lock tracker and get highest tracked entry
@@ -727,11 +731,17 @@ bool celix_serviceTracker_useHighestRankingService(service_tracker_t *tracker,
 
     for (i = 0; i < size; i++) {
         tracked = (celix_tracked_entry_t *) arrayList_get(tracker->trackedServices, i);
-        if (serviceName != NULL && tracked->serviceName != NULL && strncmp(tracked->serviceName, serviceName, 10*1024) == 0) {
-            const char *val = properties_getWithDefault(tracked->properties, OSGI_FRAMEWORK_SERVICE_RANKING, "0");
-            long rank = strtol(val, NULL, 10);
-            if (highest == NULL || rank > highestRank) {
+        if (serviceName != NULL && tracked->serviceName != NULL && celix_utils_stringEquals(tracked->serviceName, serviceName)) {
+            if (highest == NULL) {
                 highest = tracked;
+            } else {
+                int compare = celix_utils_compareServiceIdsAndRanking(
+                        tracked->serviceId, tracked->serviceRanking,
+                        highest->serviceId, highest->serviceRanking
+                );
+                if (compare < 0) {
+                    highest = tracked;
+                }
             }
         }
     }
diff --git a/libs/framework/src/service_tracker_private.h b/libs/framework/src/service_tracker_private.h
index 9580b23..49e9689 100644
--- a/libs/framework/src/service_tracker_private.h
+++ b/libs/framework/src/service_tracker_private.h
@@ -74,6 +74,8 @@ struct celix_serviceTracker {
 typedef struct celix_tracked_entry {
 	service_reference_pt reference;
 	void *service;
+    long serviceId; //cached service.id of the service
+    long serviceRanking; //cached service.ranking of the service
 	const char *serviceName;
 	properties_t *properties;
 	bundle_t *serviceOwner;
diff --git a/libs/promises/api/celix/impl/SharedPromiseState.h b/libs/promises/api/celix/impl/SharedPromiseState.h
index f92fa05..ec3f68a 100644
--- a/libs/promises/api/celix/impl/SharedPromiseState.h
+++ b/libs/promises/api/celix/impl/SharedPromiseState.h
@@ -892,7 +892,7 @@ void celix::impl::SharedPromiseState<T>::addOnFailureConsumeCallback(std::functi
             } catch (const std::exception &e) {
                 callback(e);
             } catch (...) {
-                //NOTE not an exception based on std::exception, "repacking" it to logical error
+                //NOTE not a exception based on std::exception, "repacking" it to logical error
                 std::logic_error logicError{"Unknown exception throw for the failure of A celix::Promise"};
                 callback(logicError);
             }
@@ -909,7 +909,7 @@ inline void celix::impl::SharedPromiseState<void>::addOnFailureConsumeCallback(s
             } catch (const std::exception &e) {
                 callback(e);
             } catch (...) {
-                //NOTE not an exception based on std::exception, "repacking" it to logical error
+                //NOTE not a exception based on std::exception, "repacking" it to logical error
                 std::logic_error logicError{"Unknown exception throw for the failure of A celix::Promise"};
                 callback(logicError);
             }
diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt
index 8111df3..6cb860d 100644
--- a/libs/utils/gtest/CMakeLists.txt
+++ b/libs/utils/gtest/CMakeLists.txt
@@ -21,6 +21,9 @@ add_executable(test_utils
         src/VersionRangeTestSuite.cc
         src/TimeUtilsTestSuite.cc
         src/FileUtilsTestSuite.cc
+        src/CxxUtilsTestSuite.cc
+        src/CxxPropertiesTestSuite.cc
+        src/CxxFilterTestSuite.cc
 )
 
 target_link_libraries(test_utils PRIVATE Celix::utils GTest::gtest GTest::gtest_main)
diff --git a/libs/framework/gtest/src/CxxFilterTestSuite.cc b/libs/utils/gtest/src/CxxFilterTestSuite.cc
similarity index 94%
rename from libs/framework/gtest/src/CxxFilterTestSuite.cc
rename to libs/utils/gtest/src/CxxFilterTestSuite.cc
index 4d70b2a..784a674 100644
--- a/libs/framework/gtest/src/CxxFilterTestSuite.cc
+++ b/libs/utils/gtest/src/CxxFilterTestSuite.cc
@@ -29,7 +29,7 @@ public:
 TEST_F(CxxFilterTestSuite, CreateDestroy) {
     celix::Filter filter{};
     EXPECT_TRUE(filter.empty());
-    EXPECT_TRUE(filter.getCFilter() == nullptr); //TODO tbd add support for empty filter to C?
+    EXPECT_TRUE(filter.getCFilter() == nullptr);
     EXPECT_TRUE(filter.getFilterString().empty());
 }
 
@@ -43,7 +43,7 @@ TEST_F(CxxFilterTestSuite, FilterString) {
 }
 
 TEST_F(CxxFilterTestSuite, InvalidFilter) {
-    EXPECT_THROW(celix::Filter{"bla"}, celix::Exception);
+    EXPECT_THROW(celix::Filter{"bla"}, celix::FilterException);
 }
 
 TEST_F(CxxFilterTestSuite, EmptyFilterTest) {
diff --git a/libs/framework/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc
similarity index 90%
rename from libs/framework/gtest/src/CxxPropertiesTestSuite.cc
rename to libs/utils/gtest/src/CxxPropertiesTestSuite.cc
index 1c6651e..8f151e4 100644
--- a/libs/framework/gtest/src/CxxPropertiesTestSuite.cc
+++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc
@@ -27,12 +27,12 @@ class CxxPropertiesTestSuite : public ::testing::Test {
 public:
 };
 
-TEST_F(CxxPropertiesTestSuite, CreateDestroy) {
+TEST_F(CxxPropertiesTestSuite, testCreateDestroy) {
     celix::Properties props{};
     EXPECT_EQ(0, props.size());
 }
 
-TEST_F(CxxPropertiesTestSuite, FillAndLoop) {
+TEST_F(CxxPropertiesTestSuite, testFillAndLoop) {
     celix::Properties props{};
     EXPECT_EQ(0, props.size());
 
@@ -45,7 +45,6 @@ TEST_F(CxxPropertiesTestSuite, FillAndLoop) {
 
     EXPECT_EQ(props.get("key1"), "value1");
     EXPECT_EQ(props.get("key2"), "value2");
-    //TODO ASSERT_THAT(props.get("key3"), MatchesRegex("3.3.*"));
     EXPECT_EQ(props.getAsDouble("key3", 0), 3.3);
     EXPECT_EQ(props.get("key4"), "4");
     EXPECT_EQ(props.getAsLong("key4", -1), 4);
@@ -60,7 +59,7 @@ TEST_F(CxxPropertiesTestSuite, FillAndLoop) {
     EXPECT_EQ(5, count);
 }
 
-TEST_F(CxxPropertiesTestSuite, CopyTest) {
+TEST_F(CxxPropertiesTestSuite, testCopy) {
     celix::Properties props{};
 
     props["key1"] = "value1";
@@ -75,7 +74,7 @@ TEST_F(CxxPropertiesTestSuite, CopyTest) {
     EXPECT_EQ(v2, "value1_new");
 }
 
-TEST_F(CxxPropertiesTestSuite, WrapTest) {
+TEST_F(CxxPropertiesTestSuite, testWrap) {
     auto *props = celix_properties_create();
     celix_properties_set(props, "test", "test");
 
@@ -88,4 +87,4 @@ TEST_F(CxxPropertiesTestSuite, WrapTest) {
     EXPECT_EQ(1, celix_properties_size(props));
 
     celix_properties_destroy(props);
-}
+}
\ No newline at end of file
diff --git a/libs/utils/gtest/src/CxxUtilsTestSuite.cc b/libs/utils/gtest/src/CxxUtilsTestSuite.cc
new file mode 100644
index 0000000..b98a5b0
--- /dev/null
+++ b/libs/utils/gtest/src/CxxUtilsTestSuite.cc
@@ -0,0 +1,121 @@
+/*
+ * 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 {
+    public:
+        std::string NAME; //dummy non-static NAME member, should not impact the typeName call
+        std::string VERSION; //dummy non-static VERSION member, should not impact the typeVersion call
+    };
+
+    class TestType2 {
+    public:
+        static constexpr std::string_view NAME = "AnotherTestTypeName";
+        static constexpr const char * const VERSION = "1.2.0";
+    };
+
+    class TestType3 {
+    public:
+        static constexpr int NAME = 4; //dummy static int, which should not be used as type name (cannot be used to construct a string)
+        static constexpr int VERSION = 1; //dummy static int, which should not be used as type name (cannot be used to construct a string)
+    };
+}
+
+
+TEST_F(CxxUtilsTestSuite, testTypeName) {
+    //When inferring a type name with no provided name and the type does not have a NAME static member,
+    //the call should return an inferred name based on __PRETTY__FUNCTION__ (see celix::impl::extractTypeName)
+    auto name = celix::typeName<example::TestType>();
+    EXPECT_FALSE(name.empty());
+
+    //When inferring a type name with no provided name, but the type has a NAME static member,
+    //the call should return the string value of the NAME static member
+    name = celix::typeName<example::TestType2>();
+    EXPECT_EQ(std::string{name}, std::string{"AnotherTestTypeName"});
+
+    //When inferring a type name with a provided name and the type does not have a NAME static member,
+    //the call should return the provided name.
+    name = celix::typeName<example::TestType>("OverrideName");
+    EXPECT_EQ(name, std::string{"OverrideName"});
+
+    //When inferring a type name with a provided name and the type also has a NAME static member,
+    //the call should return the provided name.
+    name = celix::typeName<example::TestType2>("OverrideName");
+    EXPECT_EQ(name, std::string{"OverrideName"});
+
+    //When inferring a type name, where there is a static NAME member but not of the right type (constructable from string),
+    //the call should return an inferred name based on __PRETTY_FUNCTION__ (see celix::impl::extractTypeName)
+    name = celix::typeName<example::TestType3>();
+    //note not testing what exactly is inferred (__PRETTY_FUNCTION__ can differ per platform)
+    EXPECT_NE(std::string{"4"}, name);
+}
+
+TEST_F(CxxUtilsTestSuite, testTypeVersion) {
+    //When inferring a type version with no provided version and the type does not have a VERSION static member,
+    //the call should return an empty version.
+    auto version = celix::typeVersion<example::TestType>();
+    EXPECT_TRUE(version.empty());
+
+    //When inferring a type version with no provided version and the type has a VERSION static member,
+    //the call should return the value of the static member VERSION.
+    version = celix::typeVersion<example::TestType2>();
+    EXPECT_EQ(std::string{version}, std::string{"1.2.0"});
+
+    //When inferring a type version with a provided version and the type does not have a VERSION static member,
+    //the call should return the provided version.
+    version = celix::typeVersion<example::TestType>("2.2.2");
+    EXPECT_EQ(version, std::string{"2.2.2"});
+
+    //When inferring a type version with a provided version and the type does have a VERSION static member,
+    //the call should return the provided version.
+    version = celix::typeVersion<example::TestType2>("2.2.2");
+    EXPECT_EQ(version, std::string{"2.2.2"});
+}
+
+TEST_F(CxxUtilsTestSuite, testSplit) {
+    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/Filter.h b/libs/utils/include/celix/Filter.h
similarity index 78%
rename from libs/framework/include/celix/Filter.h
rename to libs/utils/include/celix/Filter.h
index bb904cd..1157b91 100644
--- a/libs/framework/include/celix/Filter.h
+++ b/libs/utils/include/celix/Filter.h
@@ -23,11 +23,29 @@
 
 #include "celix_filter.h"
 #include "celix/Properties.h"
-#include "celix/Exception.h"
 
 namespace celix {
 
     /**
+     * @brief FilterException
+     */
+    class FilterException : public std::exception {
+    public:
+        explicit FilterException(std::string msg) : w{std::move(msg)} {}
+
+        FilterException(const FilterException&) = default;
+        FilterException(FilterException&&) = default;
+        FilterException& operator=(const FilterException&) = default;
+        FilterException& operator=(FilterException&&) = default;
+
+        [[nodiscard]] const char* what() const noexcept override {
+            return w.c_str();
+        }
+    private:
+        std::string w;
+    };
+
+    /**
      * @brief An RFC 1960-based (LDAP) Filter.
      *
      * Some examples:
@@ -65,7 +83,7 @@ namespace celix {
         /**
          * @brief Gets the filter string
          */
-        std::string getFilterString() const {
+        [[nodiscard]] std::string getFilterString() const {
             auto cStr = getFilterCString();
             return cStr == nullptr ? std::string{} : std::string{cStr};
         }
@@ -73,14 +91,14 @@ namespace celix {
         /**
          * @brief Get the C string. valid as long as the filter object is valid.
          */
-        const char* getFilterCString() const {
+        [[nodiscard]] const char* getFilterCString() const {
             return celix_filter_getFilterString(cFilter.get());
         }
 
         /**
          * @brief match the filter against the provided properties
          */
-        bool match(const celix::Properties& properties)  const {
+        [[nodiscard]] bool match(const celix::Properties& properties)  const {
             return celix_filter_match(cFilter.get(), properties.getCProperties());
         }
 
@@ -88,7 +106,7 @@ namespace celix {
          * @brief Find the attribute based on the provided key.
          * @return The found attribute value or an empty string if the attribute was not found.
          */
-        std::string findAttribute(const std::string& attributeKey) const {
+        [[nodiscard]] std::string findAttribute(const std::string& attributeKey) const {
             auto* cValue = celix_filter_findAttribute(cFilter.get(), attributeKey.c_str());
             return cValue == nullptr ? std::string{} : std::string{cValue};
         }
@@ -96,7 +114,7 @@ namespace celix {
         /**
          * @brief Check whether the filter has a attribute with the provided attribute key.
          */
-        bool hasAttribute(const std::string& attributeKey) const {
+        [[nodiscard]] bool hasAttribute(const std::string& attributeKey) const {
             return celix_filter_findAttribute(cFilter.get(), attributeKey.c_str()) != nullptr;
         }
 
@@ -106,14 +124,14 @@ namespace celix {
          * @warning Try not the depend on the C API from a C++ bundle. If features are missing these should be added to
          * the C++ API.
          */
-        celix_filter_t* getCFilter() const {
+        [[nodiscard]] celix_filter_t* getCFilter() const {
             return cFilter.get();
         }
 
         /**
          * @brief Return whether the filter is empty.
          */
-        bool empty() const {
+        [[nodiscard]] bool empty() const {
             return cFilter == nullptr;
         }
     private:
@@ -123,7 +141,7 @@ namespace celix {
             }
             auto* cf = celix_filter_create(filterStr.c_str());
             if (cf == nullptr) {
-                throw celix::Exception{"Invalid LDAP filter '" + filterStr + "'"};
+                throw celix::FilterException{"Invalid LDAP filter '" + filterStr + "'"};
             }
             return std::shared_ptr<celix_filter_t>{cf, [](celix_filter_t *f) {
                 celix_filter_destroy(f);
diff --git a/libs/framework/include/celix/Properties.h b/libs/utils/include/celix/Properties.h
similarity index 89%
rename from libs/framework/include/celix/Properties.h
rename to libs/utils/include/celix/Properties.h
index 0c7eaa0..7698e77 100644
--- a/libs/framework/include/celix/Properties.h
+++ b/libs/utils/include/celix/Properties.h
@@ -38,6 +38,7 @@ namespace celix {
             iter = hashMapIterator_construct((hash_map_t*)props);
             next();
         }
+
         explicit PropertiesIterator(const celix_properties_t* props) {
             iter = hashMapIterator_construct((hash_map_t*)props);
             next();
@@ -108,7 +109,7 @@ namespace celix {
                 return *this;
             }
 
-            const char* getValue() const {
+            [[nodiscard]] const char* getValue() const {
                 return celix_properties_get(props.get(), key.c_str(), nullptr);
             }
 
@@ -157,7 +158,7 @@ namespace celix {
          * @warning Try not the depend on the C API from a C++ bundle. If features are missing these should be added to
          * the C++ API.
          */
-        celix_properties_t* getCProperties() const {
+        [[nodiscard]] celix_properties_t* getCProperties() const {
             return cProps.get();
         }
 
@@ -169,16 +170,23 @@ namespace celix {
         }
 
         /**
+         * @brief Get the value for a property key
+         */
+        ValueRef operator[](std::string key) const {
+            return ValueRef{cProps, std::move(key)};
+        }
+
+        /**
          * @brief begin iterator
          */
-        const_iterator begin() const noexcept {
+        [[nodiscard]] const_iterator begin() const noexcept {
             return PropertiesIterator{cProps.get()};
         }
 
         /**
          * @brief end iterator
          */
-        const_iterator end() const noexcept {
+        [[nodiscard]] const_iterator end() const noexcept {
             auto iter = PropertiesIterator{cProps.get()};
             iter.moveToEnd();
             return iter;
@@ -187,14 +195,14 @@ namespace celix {
         /**
          * @brief constant begin iterator
          */
-        const_iterator cbegin() const noexcept {
+        [[nodiscard]] const_iterator cbegin() const noexcept {
             return PropertiesIterator{cProps.get()};
         }
 
         /**
          * @brief constant end iterator
          */
-        const_iterator cend() const noexcept {
+        [[nodiscard]] const_iterator cend() const noexcept {
             auto iter = PropertiesIterator{cProps.get()};
             iter.moveToEnd();
             return iter;
@@ -203,7 +211,7 @@ namespace celix {
         /**
          * @brief Get the value for a property key or return the defaultValue if the key does not exists.
          */
-        std::string get(const std::string& key, const std::string& defaultValue = {}) const {
+        [[nodiscard]] std::string get(const std::string& key, const std::string& defaultValue = {}) const {
             const char* found = celix_properties_get(cProps.get(), key.c_str(), nullptr);
             return found == nullptr ? std::string{defaultValue} : std::string{found};
         }
@@ -211,21 +219,21 @@ namespace celix {
         /**
          * @brief Get the value as long for a property key or return the defaultValue if the key does not exists.
          */
-        long getAsLong(const std::string& key, long defaultValue) const {
+        [[nodiscard]] long getAsLong(const std::string& key, long defaultValue) const {
             return celix_properties_getAsLong(cProps.get(), key.c_str(), defaultValue);
         }
 
         /**
          * @brief Get the value as double for a property key or return the defaultValue if the key does not exists.
          */
-        double getAsDouble(const std::string &key, double defaultValue) const {
+        [[nodiscard]] double getAsDouble(const std::string &key, double defaultValue) const {
             return celix_properties_getAsDouble(cProps.get(), key.c_str(), defaultValue);
         }
 
         /**
          * @brief Get the value as bool for a property key or return the defaultValue if the key does not exists.
          */
-        bool getAsBool(const std::string &key, bool defaultValue) const {
+        [[nodiscard]] bool getAsBool(const std::string &key, bool defaultValue) const {
             return celix_properties_getAsBool(cProps.get(), key.c_str(), defaultValue);
         }
 
@@ -262,14 +270,14 @@ namespace celix {
         /**
          * @brief Returns the nr of properties.
          */
-        std::size_t size() const {
+        [[nodiscard]] std::size_t size() const {
             return celix_properties_size(cProps.get());
         }
 
         /**
          * @brief Converts the properties a (new) std::string, std::string map.
          */
-        std::map<std::string, std::string> convertToMap() const {
+        [[nodiscard]] std::map<std::string, std::string> convertToMap() const {
             std::map<std::string, std::string> result{};
             for (const auto& pair : *this) {
                 result[pair.first] = pair.second;
@@ -280,7 +288,7 @@ namespace celix {
         /**
          * @brief Converts the properties a (new) std::string, std::string unordered map.
          */
-        std::unordered_map<std::string, std::string> convertToUnorderedMap() const {
+        [[nodiscard]] std::unordered_map<std::string, std::string> convertToUnorderedMap() const {
             std::unordered_map<std::string, std::string> result{};
             for (const auto& pair : *this) {
                 result[pair.first] = pair.second;
diff --git a/libs/utils/include/celix/Utils.h b/libs/utils/include/celix/Utils.h
new file mode 100644
index 0000000..205360d
--- /dev/null
+++ b/libs/utils/include/celix/Utils.h
@@ -0,0 +1,240 @@
+/*
+ * 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 <cstring>
+#include <iostream>
+#include <vector>
+
+//NOTE based on has_rtti.cpp
+#if defined(__clang__)
+#if __has_feature(cxx_rtti)
+#define CELIX_RTTI_ENABLED
+#endif
+#elif defined(__GNUG__)
+#if defined(__GXX_RTTI)
+#define CELIX_RTTI_ENABLED
+#endif
+#elif defined(_MSC_VER)
+#if defined(_CPPRTTI)
+#define CELIX_RTTI_ENABLED
+#endif
+#endif
+
+//FORCE DISABLE RTTI
+//TODO #323 add test CI job to test rtti based type name infer
+#undef CELIX_RTTI_ENABLED
+
+#ifdef CELIX_RTTI_ENABLED
+#include <cxxabi.h>
+#endif
+
+namespace celix {
+namespace impl {
+
+    template<typename INTERFACE_TYPENAME>
+    std::string extractTypeName() {
+        std::string result;
+#ifdef CELIX_RTTI_ENABLED
+        result = typeid(INTERFACE_TYPENAME).name();
+        int status = 0;
+        char* demangled_name {abi::__cxa_demangle(result.c_str(), NULL, NULL, &status)};
+        if(status == 0) {
+            result = std::string{demangled_name};
+            free(demangled_name);
+        }
+#else
+        const char *templateStr = "INTERFACE_TYPENAME = ";
+        const size_t templateStrLen = strlen(templateStr);
+
+        result = __PRETTY_FUNCTION__; //USING pretty function to retrieve the filled in template argument without using typeid()
+        size_t bpos = result.find(templateStr) + templateStrLen; //find begin pos after INTERFACE_TYPENAME = entry
+        size_t epos = bpos;
+        while (isalnum(result[epos]) || result[epos] == '_' || result[epos] == ':') {
+            epos += 1;
+        }
+        size_t len = epos - bpos;
+        result = result.substr(bpos, len);
+#endif
+        if (result.empty()) {
+            std::cerr << "Cannot infer type name in function call '" << __PRETTY_FUNCTION__ << "'\n'";
+        }
+        return result;
+    }
+}
+}
+
+#if __cplusplus >= 201703L //C++17 or higher
+namespace celix::impl {
+
+    template<typename T>
+    constexpr typename std::enable_if_t<std::is_constructible_v<std::string, T>, bool>
+    canBeConstructedWithString() { return true; }
+
+    template<typename T>
+    constexpr typename std::enable_if_t<not std::is_constructible_v<std::string, T>, bool>
+    canBeConstructedWithString() { return false; }
+
+    template<typename T>
+    constexpr typename std::enable_if_t<std::is_member_object_pointer_v<decltype(&T::NAME)>, bool>
+    isNameMemberStatic() { return false; }
+
+    template<typename T>
+    constexpr typename std::enable_if_t<!std::is_member_object_pointer_v<decltype(&T::NAME)>, bool>
+    isNameMemberStatic() { return canBeConstructedWithString<decltype(T::NAME)>(); }
+
+    template<typename, typename = void>
+    constexpr bool hasStaticMemberName = false;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-value"
+    /**
+     * Partial specialization only possible (and chosen) when late binding of decltype succeeds ->
+     *  i.e. when T::NAME exists.
+     * note T::NAME is unused, so suppressing unused-value warning
+     */
+    template<typename T>
+    constexpr bool hasStaticMemberName<T, decltype(T::NAME,void())> = isNameMemberStatic<T>();
+#pragma GCC diagnostic pop
+
+    template<typename T>
+    constexpr typename std::enable_if_t<std::is_member_object_pointer_v<decltype(&T::VERSION)>, bool>
+    isVersionMemberStatic() { return false; }
+
+    template<typename T>
+    constexpr typename std::enable_if_t<not std::is_member_object_pointer_v<decltype(&T::VERSION)>, bool>
+    isVersionMemberStatic() { return canBeConstructedWithString<decltype(T::VERSION)>(); }
+
+    template<typename, typename = void>
+    constexpr bool hasStaticMemberVersion = false;
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-value"
+    /**
+     * Partial specialization only possible (and chosen) when late binding of decltype succeeds ->
+     *  i.e. when T::VERSION exists.
+     * note T::VERSION is unused, so suppressing unused-value warning
+     */
+    template<typename T>
+    constexpr bool hasStaticMemberVersion<T, decltype(T::VERSION,void())> = isVersionMemberStatic<T>();
+#pragma GCC diagnostic pop
+}
+
+namespace celix {
+    /**
+     * @brief Returns the inferred type name for the template I if the providedTypeName is empty.
+     *
+     * If the a non empty providedTypeName is provided this will be returned.
+     * If the provideTypeName is empty and a static constexpr and accessible member I::NAME exists,
+     * that will be returned.
+     * Otherwise the celix::impl::typeName will be used to infer the type name.
+     * celix::impl::typeName uses the macro __PRETTY_FUNCTION__ to extract a type name.
+     */
+    template<typename I>
+    std::string typeName(const std::string& providedTypeName = "") {
+        if (!providedTypeName.empty()) {
+            return providedTypeName;
+        } else if constexpr (celix::impl::hasStaticMemberName<I>) {
+            return std::string{I::NAME};
+        } else {
+            return celix::impl::extractTypeName<I>();
+        }
+    }
+
+    /**
+     * @brief Returns the inferred type version for the template I if the providedVersion is empty.
+     *
+     * If the a non empty providedVersion is provided this will be returned.
+     * If the providedVersion is empty and a static constexpr and accessible member I::VERSION exists,
+     * that will be returned.
+     * Otherwise a empty string will be returned.
+     */
+    template<typename I>
+    std::string typeVersion(const std::string& providedVersion = "") {
+        if (!providedVersion.empty()) {
+            return providedVersion;
+        } else if constexpr (celix::impl::hasStaticMemberVersion<I>) {
+            return std::string{I::VERSION};
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     * @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;
+     }
+}
+
+#else //Assuming at least C++11
+
+namespace celix {
+    /**
+     * @brief Returns the inferred type name for the template I if the providedTypeName is empty.
+     *
+     * If the a non empty providedTypeName is provided this will be returned.
+     * Otherwise the celix::impl::typeName will be used to infer the type name.
+     * celix::impl::typeName uses the macro __PRETTY_FUNCTION__ to extract a type name.
+     */
+    template<typename I>
+    std::string typeName(const std::string &providedTypeName = "") {
+        if (!providedTypeName.empty()) {
+            return providedTypeName;
+        } else {
+            return celix::impl::extractTypeName<I>();
+        }
+    }
+
+    /**
+     * @brief Returns the type version.
+     *
+     * Returns the provideVersion if not empty or an empty string.
+     * Note that this function does not do anything, but is signature
+     * compatible with the C++17 celix::typeName<I>() version.
+     */
+    template<typename I>
+    std::string typeVersion(const std::string& providedVersion = "") {
+        if (!providedVersion.empty()) {
+            return providedVersion;
+        } else {
+            return "";
+        }
+    }
+}
+
+#endif
+
+#undef CELIX_RTTI_ENABLED
\ No newline at end of file