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:50 UTC

[celix] branch feature/cxx17_headers created (now a620a2e)

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

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


      at a620a2e  Updates C++ header only to C++17, except the for already existing C++ Dep Man headers

This branch includes the following new commits:

     new a620a2e  Updates C++ header only to C++17, except the for already existing C++ Dep Man headers

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


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

Posted by pn...@apache.org.
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