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/01/03 20:10:24 UTC

[celix] 01/01: Adds initial impl for the C++ headers only wrappers

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

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

commit 0b0e894de92aa7a4d7252c30be4b47d6957c9a34
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Fri Nov 20 19:17:55 2020 +0100

    Adds initial impl for the C++ headers only wrappers
---
 .../http_admin/test/test/http_websocket_tests.cc   |   1 +
 .../log_service_v2/src/log_service_activator.c     |   2 -
 bundles/shell/shell/src/activator.c                |   1 -
 .../getting_started/creating_a_simple_bundle.md    |  28 +-
 examples/celix-examples/CMakeLists.txt             |   2 +
 .../dm_example_cxx/phase1/src/Phase1Activator.cc   |   2 +-
 .../dm_example_cxx/phase2/src/Phase2aActivator.cc  |   2 +-
 .../dm_example_cxx/phase2/src/Phase2bActivator.cc  |   3 +-
 .../dm_example_cxx/phase3/src/Phase3Activator.cc   |   2 +-
 .../phase3_locking/src/Phase3LockingActivator.cc   |   2 +-
 .../hello_world_cxx/src/BundleActivator.cc         |  14 +-
 .../services_example_cxx/CMakeLists.txt            |  38 +-
 .../celix-examples/services_example_cxx/README.md  |  22 +
 .../services_example_cxx/api/examples/ICalc.h      |  13 +-
 .../src/DynamicConsumerBundleActivator.cc          | 152 ++++++
 .../src/SimpleConsumerBundleActivator.cc           |  90 ++++
 .../src/SimpleProviderBundleActivator.cc           |  55 ++
 .../CMakeLists.txt                                 |   0
 .../api/IAnotherExample.h                          |   0
 .../api/example.h                                  |   0
 .../bar/CMakeLists.txt                             |   0
 .../bar/src/Bar.cc                                 |   0
 .../bar/src/Bar.h                                  |   0
 .../bar/src/BarActivator.cc                        |   2 +-
 .../bar/src/BarActivator.h                         |   0
 .../baz/CMakeLists.txt                             |   0
 .../baz/src/Baz.cc                                 |   0
 .../baz/src/Baz.h                                  |   0
 .../baz/src/BazActivator.cc                        |   2 +-
 .../baz/src/BazActivator.h                         |   0
 .../foo/CMakeLists.txt                             |   0
 .../foo/src/Foo.cc                                 |   0
 .../foo/src/Foo.h                                  |   0
 .../foo/src/FooActivator.cc                        |   2 +-
 .../foo/src/FooActivator.h                         |   0
 libs/framework/gtest/CMakeLists.txt                |   9 +-
 .../framework/gtest/src/CxxBundleContext_tests.cpp | 435 +++++++++++++++
 libs/framework/gtest/src/CxxFilter_tests.cpp       |  75 +++
 libs/framework/gtest/src/CxxProperties_tests.cpp   |  91 ++++
 .../gtest/src/DependencyManagerTestSuite.cc        |   3 +-
 .../src/HelloWorldCxxActivator.cpp}                |  16 +-
 .../gtest/src/bundle_context_services_test.cpp     |  28 +-
 libs/framework/include/celix/Builders.h            | 600 +++++++++++++++++++++
 .../framework/include/celix/Bundle.h               |  40 +-
 libs/framework/include/celix/BundleActivator.h     | 105 ++++
 libs/framework/include/celix/BundleContext.h       | 505 +++++++++++++++++
 libs/framework/include/celix/Constants.h           |  40 ++
 libs/framework/include/celix/Filter.h              |  91 ++++
 .../framework/include/celix/Framework.h            |  47 +-
 libs/framework/include/celix/Properties.h          | 217 ++++++++
 .../celix/{dm/Properties.h => ServiceFactory.h}    |  19 +-
 libs/framework/include/celix/ServiceRegistration.h | 176 ++++++
 libs/framework/include/celix/Trackers.h            | 481 +++++++++++++++++
 .../include/celix/{dm/types.h => Utils.h}          |  37 +-
 libs/framework/include/celix/dm/Component.h        |  16 +-
 libs/framework/include/celix/dm/Component_Impl.h   |  13 -
 .../framework/include/celix/dm/DependencyManager.h |   1 -
 .../include/celix/dm/DependencyManager_Impl.h      |   1 -
 libs/framework/include/celix/dm/Properties.h       |   5 +-
 libs/framework/include/celix/dm/ProvidedService.h  |   1 -
 .../include/celix/dm/ProvidedService_Impl.h        |   2 -
 .../framework/include/celix/dm/ServiceDependency.h |  15 +-
 .../include/celix/dm/ServiceDependency_Impl.h      |  45 +-
 libs/framework/include/celix/dm/types.h            |  43 +-
 libs/framework/include/celix_api.h                 |   4 +-
 libs/framework/include/celix_bundle_activator.h    |  68 ---
 libs/framework/include/celix_bundle_context.h      |  21 +-
 libs/framework/include/celix_constants.h           |  72 ++-
 libs/framework/include/celix_log.h                 |   2 +
 libs/framework/include/dm_component.h              |   3 +-
 libs/framework/include/service_registry.h          |   4 +-
 libs/framework/src/bundle_context.c                |  19 +-
 libs/framework/src/celix_log.c                     |   6 +-
 libs/framework/src/dm_component_impl.c             |  11 +-
 libs/framework/src/dm_service_dependency.c         |  17 -
 libs/framework/src/service_registry.c              |  33 +-
 libs/framework/src/service_tracker.c               |   2 +-
 libs/utils/src/filter.c                            |   2 +-
 libs/utils/src/properties.c                        |   2 +-
 79 files changed, 3416 insertions(+), 442 deletions(-)

diff --git a/bundles/http_admin/test/test/http_websocket_tests.cc b/bundles/http_admin/test/test/http_websocket_tests.cc
index 9ea73ea..1db9f74 100644
--- a/bundles/http_admin/test/test/http_websocket_tests.cc
+++ b/bundles/http_admin/test/test/http_websocket_tests.cc
@@ -27,6 +27,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <string.h>
 
 #include "celix_api.h"
 #include "http_admin/api.h"
diff --git a/bundles/logging/log_service_v2/src/log_service_activator.c b/bundles/logging/log_service_v2/src/log_service_activator.c
index c3cf9f9..6570218 100644
--- a/bundles/logging/log_service_v2/src/log_service_activator.c
+++ b/bundles/logging/log_service_v2/src/log_service_activator.c
@@ -109,7 +109,6 @@ celix_status_t bundleActivator_start(void * userData, celix_bundle_context_t *co
     logFactory_create(activator->logger, &activator->factory);
 
 	celix_properties_t *props = celix_properties_create();
-	celix_properties_set(props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, CELIX_FRAMEWORK_SERVICE_C_LANGUAGE);
 
 
 	bundleContext_registerServiceFactory(context, (char *) OSGI_LOGSERVICE_NAME, activator->factory, props, &activator->logServiceFactoryReg);
@@ -124,7 +123,6 @@ celix_status_t bundleActivator_start(void * userData, celix_bundle_context_t *co
     activator->reader_service->removeAllLogListener = logReaderService_removeAllLogListener;
 
 	props = celix_properties_create();
-	celix_properties_set(props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, CELIX_FRAMEWORK_SERVICE_C_LANGUAGE);
 
     bundleContext_registerService(context, (char *) OSGI_LOGSERVICE_READER_SERVICE_NAME, activator->reader_service, props, &activator->logReaderServiceReg);
     return status;
diff --git a/bundles/shell/shell/src/activator.c b/bundles/shell/shell/src/activator.c
index b33667e..8675423 100644
--- a/bundles/shell/shell/src/activator.c
+++ b/bundles/shell/shell/src/activator.c
@@ -173,7 +173,6 @@ celix_status_t bundleActivator_create(celix_bundle_context_t* ctx, void **_pptr)
             celix_properties_set(activator->std_commands[i].props, CELIX_SHELL_COMMAND_NAME, activator->std_commands[i].name);
             celix_properties_set(activator->std_commands[i].props, CELIX_SHELL_COMMAND_USAGE, activator->std_commands[i].usage);
             celix_properties_set(activator->std_commands[i].props, CELIX_SHELL_COMMAND_DESCRIPTION, activator->std_commands[i].description);
-            celix_properties_set(activator->std_commands[i].props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, CELIX_FRAMEWORK_SERVICE_C_LANGUAGE);
 
             activator->std_commands[i].service.handle = ctx;
             activator->std_commands[i].service.executeCommand = activator->std_commands[i].exec;
diff --git a/documents/getting_started/creating_a_simple_bundle.md b/documents/getting_started/creating_a_simple_bundle.md
index 56375b4..b992e36 100644
--- a/documents/getting_started/creating_a_simple_bundle.md
+++ b/documents/getting_started/creating_a_simple_bundle.md
@@ -165,25 +165,21 @@ The C++ Bundle Activator:
 #include <memory>
 #include <iostream>
 
-#include <celix_api.h>
+#include "celix/BundleActivator.h"
 
 namespace /*anon*/ {
 
-    class BundleActivator {
-    public:
-        BundleActivator(std::shared_ptr<celix::dm::DependencyManager> _mng) : mng{_mng} {
-            std::cout << "Hello world from C++ bundle with id " << bndId() << std::endl;
-        }
-        ~BundleActivator() {
-            std::cout << "Goodbye world from C++ bundle with id " << bndId() << std::endl;
-        }
-    private:
-        long bndId() const {
-            return celix_bundle_getId(celix_bundleContext_getBundle(mng->bundleContext()));
-        }
-
-        std::shared_ptr<celix::dm::DependencyManager> mng;
-    };
+	class BundleActivator {
+	public:
+		explicit BundleActivator(std::shared_ptr<celix::BundleContext> _ctx) : ctx{std::move(_ctx)} {
+			std::cout << "Hello world from C++ bundle with id " << ctx->getBundle().getId() << std::endl;
+		}
+		~BundleActivator() {
+			std::cout << "Goodbye world from C++ bundle with id " << ctx->getBundle().getId() << std::endl;
+		}
+	private:
+		const std::shared_ptr<celix::BundleContext> ctx;
+	};
 
 }
 
diff --git a/examples/celix-examples/CMakeLists.txt b/examples/celix-examples/CMakeLists.txt
index 7e06819..b25a139 100644
--- a/examples/celix-examples/CMakeLists.txt
+++ b/examples/celix-examples/CMakeLists.txt
@@ -32,6 +32,8 @@ if (EXAMPLES)
     add_subdirectory(services_example_c)
     add_subdirectory(services_example_cxx)
 
+    add_subdirectory(svc_depependency_example_cxx)
+
     #TODO refactor export_import
     #add_subdirectory(export_import)
 
diff --git a/examples/celix-examples/dm_example_cxx/phase1/src/Phase1Activator.cc b/examples/celix-examples/dm_example_cxx/phase1/src/Phase1Activator.cc
index 52ede08..8984b66 100644
--- a/examples/celix-examples/dm_example_cxx/phase1/src/Phase1Activator.cc
+++ b/examples/celix-examples/dm_example_cxx/phase1/src/Phase1Activator.cc
@@ -20,7 +20,7 @@
 #include "Phase1Cmp.h"
 #include "Phase1Activator.h"
 #include "IPhase2.h"
-#include <celix_api.h>
+#include <celix/BundleActivator.h>
 
 using namespace celix::dm;
 
diff --git a/examples/celix-examples/dm_example_cxx/phase2/src/Phase2aActivator.cc b/examples/celix-examples/dm_example_cxx/phase2/src/Phase2aActivator.cc
index 2d0b20f..b3bc429 100644
--- a/examples/celix-examples/dm_example_cxx/phase2/src/Phase2aActivator.cc
+++ b/examples/celix-examples/dm_example_cxx/phase2/src/Phase2aActivator.cc
@@ -18,7 +18,7 @@
  */
 
 #include <IName.h>
-#include <celix_api.h>
+#include <celix/BundleActivator.h>
 #include "Phase2Cmp.h"
 #include "Phase2Activator.h"
 #include "celix_log_service.h"
diff --git a/examples/celix-examples/dm_example_cxx/phase2/src/Phase2bActivator.cc b/examples/celix-examples/dm_example_cxx/phase2/src/Phase2bActivator.cc
index c64b9a8..7ac3bc2 100644
--- a/examples/celix-examples/dm_example_cxx/phase2/src/Phase2bActivator.cc
+++ b/examples/celix-examples/dm_example_cxx/phase2/src/Phase2bActivator.cc
@@ -20,7 +20,8 @@
 #include "Phase2Cmp.h"
 #include "Phase2Activator.h"
 #include "celix_log_service.h"
-#include <celix_bundle_activator.h>
+#include <celix/BundleActivator.h>
+
 
 using namespace celix::dm;
 
diff --git a/examples/celix-examples/dm_example_cxx/phase3/src/Phase3Activator.cc b/examples/celix-examples/dm_example_cxx/phase3/src/Phase3Activator.cc
index d6e67ca..3c3e0e3 100644
--- a/examples/celix-examples/dm_example_cxx/phase3/src/Phase3Activator.cc
+++ b/examples/celix-examples/dm_example_cxx/phase3/src/Phase3Activator.cc
@@ -20,7 +20,7 @@
 
 #include "Phase3Cmp.h"
 #include "Phase3Activator.h"
-#include <celix_api.h>
+#include <celix/BundleActivator.h>
 
 using namespace celix::dm;
 
diff --git a/examples/celix-examples/dm_example_cxx/phase3_locking/src/Phase3LockingActivator.cc b/examples/celix-examples/dm_example_cxx/phase3_locking/src/Phase3LockingActivator.cc
index 23c98a9..7ae909e 100644
--- a/examples/celix-examples/dm_example_cxx/phase3_locking/src/Phase3LockingActivator.cc
+++ b/examples/celix-examples/dm_example_cxx/phase3_locking/src/Phase3LockingActivator.cc
@@ -22,7 +22,7 @@
 #include "Phase3LockingActivator.h"
 
 #include <memory>
-#include <celix_api.h>
+#include <celix/BundleActivator.h>
 
 using namespace celix::dm;
 
diff --git a/examples/celix-examples/hello_world_cxx/src/BundleActivator.cc b/examples/celix-examples/hello_world_cxx/src/BundleActivator.cc
index 2921ba7..8606444 100644
--- a/examples/celix-examples/hello_world_cxx/src/BundleActivator.cc
+++ b/examples/celix-examples/hello_world_cxx/src/BundleActivator.cc
@@ -20,24 +20,20 @@
 #include <memory>
 #include <iostream>
 
-#include <celix_api.h>
+#include "celix/BundleActivator.h"
 
 namespace /*anon*/ {
 
     class BundleActivator {
     public:
-        BundleActivator(std::shared_ptr<celix::dm::DependencyManager> _mng) : mng{std::move(_mng)} {
-            std::cout << "Hello world from C++ bundle with id " << bndId() << std::endl;
+        explicit BundleActivator(std::shared_ptr<celix::BundleContext> _ctx) : ctx{std::move(_ctx)} {
+            std::cout << "Hello world from C++ bundle with id " << ctx->getBundle().getId() << std::endl;
         }
         ~BundleActivator() {
-            std::cout << "Goodbye world from C++ bundle with id " << bndId() << std::endl;
+            std::cout << "Goodbye world from C++ bundle with id " << ctx->getBundle().getId() << std::endl;
         }
     private:
-        long bndId() const {
-            return celix_bundle_getId(celix_bundleContext_getBundle(mng->bundleContext()));
-        }
-
-        std::shared_ptr<celix::dm::DependencyManager> mng;
+        const std::shared_ptr<celix::BundleContext> ctx;
     };
 
 }
diff --git a/examples/celix-examples/services_example_cxx/CMakeLists.txt b/examples/celix-examples/services_example_cxx/CMakeLists.txt
index 3bf458a..c8709d9 100644
--- a/examples/celix-examples/services_example_cxx/CMakeLists.txt
+++ b/examples/celix-examples/services_example_cxx/CMakeLists.txt
@@ -5,9 +5,9 @@
 # to you under the Apache License, Version 2.0 (the
 # "License"); you may not use this file except in compliance
 # with the License.  You may obtain a copy of the License at
-#
+# 
 #   http://www.apache.org/licenses/LICENSE-2.0
-#
+# 
 # Unless required by applicable law or agreed to in writing,
 # software distributed under the License is distributed on an
 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -15,23 +15,29 @@
 # specific language governing permissions and limitations
 # under the License.
 
-add_library(services_example_cxx_api INTERFACE)
-target_include_directories(services_example_cxx_api INTERFACE api)
+add_library(ServiceExamplesApi INTERFACE)
+target_include_directories(ServiceExamplesApi INTERFACE api)
 
-add_subdirectory(bar)
-add_subdirectory(foo)
-add_subdirectory(baz)
+add_celix_bundle(SimpleProvider SOURCES src/SimpleProviderBundleActivator.cc VERSION 1.0.0)
+target_link_libraries(SimpleProvider PRIVATE ServiceExamplesApi)
 
-add_celix_container(services_example_cxx
-    GROUP services_example
-    COPY
-    BUNDLES
+add_celix_bundle(SimpleConsumer SOURCES src/SimpleConsumerBundleActivator.cc VERSION 1.0.0)
+target_link_libraries(SimpleConsumer PRIVATE ServiceExamplesApi)
+
+add_celix_container(SimpleServicesExample CXX BUNDLES
         Celix::shell
         Celix::shell_tui
-        bar_cxx
-        foo_cxx
-        baz_cxx
-    PROPERTIES
-        example=value
+        SimpleProvider
+        SimpleConsumer
 )
 
+
+add_celix_bundle(DynamicConsumer SOURCES src/DynamicConsumerBundleActivator.cc VERSION 1.0.0)
+target_link_libraries(DynamicConsumer PRIVATE ServiceExamplesApi)
+
+add_celix_container(DynamicServicesExample CXX BUNDLES
+        Celix::shell
+        Celix::shell_tui
+        SimpleProvider
+        DynamicConsumer
+)
\ No newline at end of file
diff --git a/examples/celix-examples/services_example_cxx/README.md b/examples/celix-examples/services_example_cxx/README.md
new file mode 100644
index 0000000..d0622c8
--- /dev/null
+++ b/examples/celix-examples/services_example_cxx/README.md
@@ -0,0 +1,22 @@
+---
+title: Services Example C++
+---
+
+<!--
+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.
+-->
+
+TODO
\ No newline at end of file
diff --git a/libs/framework/include/celix/dm/Properties.h b/examples/celix-examples/services_example_cxx/api/examples/ICalc.h
similarity index 85%
copy from libs/framework/include/celix/dm/Properties.h
copy to examples/celix-examples/services_example_cxx/api/examples/ICalc.h
index 5c33885..91dd7b7 100644
--- a/libs/framework/include/celix/dm/Properties.h
+++ b/examples/celix-examples/services_example_cxx/api/examples/ICalc.h
@@ -19,9 +19,12 @@
 
 #pragma once
 
-#include <map>
-#include <string>
+namespace examples {
 
-namespace celix { namespace dm {
-    using Properties = std::map<std::string, std::string>;
-}}
+    class ICalc {
+    public:
+        virtual ~ICalc() = default;
+        virtual int calc(int input) = 0;
+    };
+
+}
\ No newline at end of file
diff --git a/examples/celix-examples/services_example_cxx/src/DynamicConsumerBundleActivator.cc b/examples/celix-examples/services_example_cxx/src/DynamicConsumerBundleActivator.cc
new file mode 100644
index 0000000..086f4c7
--- /dev/null
+++ b/examples/celix-examples/services_example_cxx/src/DynamicConsumerBundleActivator.cc
@@ -0,0 +1,152 @@
+/*
+ * 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 <utility>
+
+#include "celix/BundleActivator.h"
+#include "examples/ICalc.h"
+
+namespace /*anon*/ {
+
+    class DynamicConsumer {
+    public:
+        explicit DynamicConsumer(int _id) : id{_id} {}
+
+        ~DynamicConsumer() {
+            active = false;
+            calcThread.join();
+            auto msg = std::string{"Destroying consumer nr "} + std::to_string(id) + "\n";
+            std::cout << msg;
+        }
+
+        void start() {
+            std::lock_guard<std::mutex> lck{mutex};
+            calcThread = std::thread{&DynamicConsumer::run, this};
+        }
+
+        void setCalc(std::shared_ptr<examples::ICalc> _calc) {
+            std::lock_guard<std::mutex> lck{mutex};
+            calc = std::move(_calc);
+        }
+    private:
+        void run() {
+  //          std::unique_lock<std::mutex> lck{mutex,  std::defer_lock};
+            int count = 1;
+            while (active) {
+//                lck.lock();
+                auto localCalc = calc;
+//                lck.unlock();
+
+                /*
+                 * note it is safe to use the localCalc outside a mutex,
+                 * because the shared_prt count will ensure the service cannot be unregistered while in use.
+                 */
+                if (localCalc) {
+                    auto msg = std::string{"Calc result for consumer nr "} + std::to_string(id) + " with input " + std::to_string(count) + " is " + std::to_string(localCalc->calc(count)) + "\n";
+                    std::cout << msg;
+                } else {
+                    auto msg = std::string{"Calc service not available for consumer nr "} + std::to_string(id) + "\n";
+                    std::cout << msg;
+                }
+
+                //NOTE the sleep also keeps the localCalc shared_ptr activate and as result block the removal of the service
+                std::this_thread::sleep_for(std::chrono::seconds{2});
+
+                ++count;
+            }
+        }
+
+        const int id;
+        std::atomic<bool> active{true};
+        std::shared_ptr<examples::ICalc> calc{};
+
+        std::mutex mutex{}; //protects below
+        std::thread calcThread{};
+    };
+
+    class DynamicConsumerFactory {
+    public:
+        static constexpr std::size_t MAX_CONSUMERS = 5;
+
+        explicit DynamicConsumerFactory(std::shared_ptr<celix::BundleContext>  _ctx) : ctx{std::move(_ctx)} {}
+
+        ~DynamicConsumerFactory() {
+            ctx->logInfo("Destroying DynamicConsumerFactory");
+            active = false;
+            factoryThread.join();
+        }
+
+
+        void start() {
+            std::lock_guard<std::mutex> lck{mutex};
+            factoryThread = std::thread{&DynamicConsumerFactory::run, this};
+        }
+
+        void run() {
+            std::unique_lock<std::mutex> lck{mutex,  std::defer_lock};
+            int nextConsumerId = 1;
+            while (active) {
+                lck.lock();
+                if (consumers.size() < MAX_CONSUMERS) {
+                    ctx->logInfo("Creating dynamic consumer nr %i", consumers.size());
+                    auto consumer = std::make_shared<DynamicConsumer>(nextConsumerId++);
+                    trackers.push_back(createTracker(ctx, consumer)); //NOTE is this possible after a move?
+                    consumers.push_back(consumer);
+                    consumer->start();
+                } else {
+                    ctx->logInfo("Resetting all trackers and consumers");
+                    trackers.clear();
+                    ctx->logInfo("Done resetting trackers");
+                    consumers.clear();
+                    ctx->logInfo("Done resetting consumer");
+                }
+                lck.unlock();
+                std::this_thread::sleep_for(std::chrono::seconds{1});
+            }
+        }
+    private:
+        static std::shared_ptr<celix::GenericServiceTracker> createTracker(const std::shared_ptr<celix::BundleContext>& ctx, const std::shared_ptr<DynamicConsumer>& consumer) {
+            return ctx->trackServices<examples::ICalc>()
+                    .addSetCallback([consumer](std::shared_ptr<examples::ICalc> svc) {
+                        consumer->setCalc(std::move(svc));
+                    })
+                    .build();
+        }
+
+        const std::shared_ptr<celix::BundleContext> ctx;
+        std::atomic<bool> active{true};
+
+        std::mutex mutex{}; //protects below
+        std::thread factoryThread{};
+        std::vector<std::shared_ptr<DynamicConsumer>> consumers{};
+        std::vector<std::shared_ptr<celix::GenericServiceTracker>> trackers{};
+    };
+
+    class DynamicConsumerBundleActivator {
+    public:
+        explicit DynamicConsumerBundleActivator(const std::shared_ptr<celix::BundleContext>& ctx) : factory{ctx} {
+            factory.start();
+        }
+    private:
+        DynamicConsumerFactory factory;
+    };
+
+}
+
+CELIX_GEN_CXX_BUNDLE_ACTIVATOR(DynamicConsumerBundleActivator)
\ No newline at end of file
diff --git a/examples/celix-examples/services_example_cxx/src/SimpleConsumerBundleActivator.cc b/examples/celix-examples/services_example_cxx/src/SimpleConsumerBundleActivator.cc
new file mode 100644
index 0000000..713b287
--- /dev/null
+++ b/examples/celix-examples/services_example_cxx/src/SimpleConsumerBundleActivator.cc
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "celix/BundleActivator.h"
+#include "examples/ICalc.h"
+
+namespace /*anon*/ {
+
+    class SimpleConsumer {
+    public:
+        ~SimpleConsumer() {
+            active = false;
+            calcThread.join();
+        }
+
+        void start() {
+            std::lock_guard<std::mutex> lck{mutex};
+            calcThread = std::thread{&SimpleConsumer::run, this};
+        }
+
+        void setCalc(std::shared_ptr<examples::ICalc> _calc) {
+            std::lock_guard<std::mutex> lck{mutex};
+            calc = std::move(_calc);
+        }
+    private:
+        void run() {
+            std::unique_lock<std::mutex> lck{mutex,  std::defer_lock};
+            int count = 1;
+            while (active) {
+                lck.lock();
+                auto localCalc = calc;
+                lck.unlock();
+
+                /*
+                 * note it is safe to use the localCalc outside a mutex,
+                 * because the shared_prt count will ensure the service cannot be unregistered while in use.
+                 */
+                if (localCalc) {
+                    std::cout << "Calc result for input " << count << " is " << localCalc->calc(count) << std::endl;
+                } else {
+                    std::cout << "Calc service not available!" << std::endl;
+                }
+                std::this_thread::sleep_for(std::chrono::seconds{2});
+                ++count;
+            }
+        }
+
+        std::atomic<bool> active{true};
+
+        std::mutex mutex{}; //protects below
+        std::shared_ptr<examples::ICalc> calc{};
+        std::thread calcThread{};
+    };
+
+    class SimpleConsumerBundleActivator {
+    public:
+        explicit SimpleConsumerBundleActivator(const std::shared_ptr<celix::BundleContext>& ctx) :
+                tracker{createTracker(ctx)} {
+            consumer.start();
+        }
+    private:
+        std::shared_ptr<celix::GenericServiceTracker> createTracker(const std::shared_ptr<celix::BundleContext>& ctx) {
+            return ctx->trackServices<examples::ICalc>()
+                    .addSetCallback(std::bind(&SimpleConsumer::setCalc, &consumer, std::placeholders::_1))
+                    .build();
+        }
+
+        const std::shared_ptr<celix::GenericServiceTracker> tracker;
+        SimpleConsumer consumer{};
+    };
+
+}
+
+CELIX_GEN_CXX_BUNDLE_ACTIVATOR(SimpleConsumerBundleActivator)
\ No newline at end of file
diff --git a/examples/celix-examples/services_example_cxx/src/SimpleProviderBundleActivator.cc b/examples/celix-examples/services_example_cxx/src/SimpleProviderBundleActivator.cc
new file mode 100644
index 0000000..efde6f2
--- /dev/null
+++ b/examples/celix-examples/services_example_cxx/src/SimpleProviderBundleActivator.cc
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "celix/BundleActivator.h"
+#include "examples/ICalc.h"
+
+namespace /*anon*/ {
+
+    class Calc : public examples::ICalc {
+    public:
+        explicit Calc(int _seed) : seed{_seed} {}
+        ~Calc() override = default;
+
+        int calc(int input) override {
+            return seed * input;
+        }
+    private:
+        const int seed;
+    };
+
+    class SimpleProviderBundleActivator {
+    public:
+        explicit SimpleProviderBundleActivator(std::shared_ptr<celix::BundleContext> ctx) :
+            registration{createCalcService(ctx)} {}
+
+    private:
+        static std::shared_ptr<celix::ServiceRegistration> createCalcService(std::shared_ptr<celix::BundleContext>& ctx) {
+            int seed = 42;
+            return ctx->registerService<examples::ICalc>(std::make_shared<Calc>(seed))
+                    .addProperty("seed", seed)
+                    .build();
+        }
+
+        const std::shared_ptr<celix::ServiceRegistration> registration;
+    };
+
+}
+
+CELIX_GEN_CXX_BUNDLE_ACTIVATOR(SimpleProviderBundleActivator)
\ No newline at end of file
diff --git a/examples/celix-examples/services_example_cxx/CMakeLists.txt b/examples/celix-examples/svc_depependency_example_cxx/CMakeLists.txt
similarity index 100%
copy from examples/celix-examples/services_example_cxx/CMakeLists.txt
copy to examples/celix-examples/svc_depependency_example_cxx/CMakeLists.txt
diff --git a/examples/celix-examples/services_example_cxx/api/IAnotherExample.h b/examples/celix-examples/svc_depependency_example_cxx/api/IAnotherExample.h
similarity index 100%
rename from examples/celix-examples/services_example_cxx/api/IAnotherExample.h
rename to examples/celix-examples/svc_depependency_example_cxx/api/IAnotherExample.h
diff --git a/examples/celix-examples/services_example_cxx/api/example.h b/examples/celix-examples/svc_depependency_example_cxx/api/example.h
similarity index 100%
rename from examples/celix-examples/services_example_cxx/api/example.h
rename to examples/celix-examples/svc_depependency_example_cxx/api/example.h
diff --git a/examples/celix-examples/services_example_cxx/bar/CMakeLists.txt b/examples/celix-examples/svc_depependency_example_cxx/bar/CMakeLists.txt
similarity index 100%
rename from examples/celix-examples/services_example_cxx/bar/CMakeLists.txt
rename to examples/celix-examples/svc_depependency_example_cxx/bar/CMakeLists.txt
diff --git a/examples/celix-examples/services_example_cxx/bar/src/Bar.cc b/examples/celix-examples/svc_depependency_example_cxx/bar/src/Bar.cc
similarity index 100%
rename from examples/celix-examples/services_example_cxx/bar/src/Bar.cc
rename to examples/celix-examples/svc_depependency_example_cxx/bar/src/Bar.cc
diff --git a/examples/celix-examples/services_example_cxx/bar/src/Bar.h b/examples/celix-examples/svc_depependency_example_cxx/bar/src/Bar.h
similarity index 100%
rename from examples/celix-examples/services_example_cxx/bar/src/Bar.h
rename to examples/celix-examples/svc_depependency_example_cxx/bar/src/Bar.h
diff --git a/examples/celix-examples/services_example_cxx/bar/src/BarActivator.cc b/examples/celix-examples/svc_depependency_example_cxx/bar/src/BarActivator.cc
similarity index 95%
rename from examples/celix-examples/services_example_cxx/bar/src/BarActivator.cc
rename to examples/celix-examples/svc_depependency_example_cxx/bar/src/BarActivator.cc
index 0da8e29..9c26772 100644
--- a/examples/celix-examples/services_example_cxx/bar/src/BarActivator.cc
+++ b/examples/celix-examples/svc_depependency_example_cxx/bar/src/BarActivator.cc
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-#include <celix_api.h>
+#include <celix/BundleActivator.h>
 
 #include "Bar.h"
 #include "BarActivator.h"
diff --git a/examples/celix-examples/services_example_cxx/bar/src/BarActivator.h b/examples/celix-examples/svc_depependency_example_cxx/bar/src/BarActivator.h
similarity index 100%
rename from examples/celix-examples/services_example_cxx/bar/src/BarActivator.h
rename to examples/celix-examples/svc_depependency_example_cxx/bar/src/BarActivator.h
diff --git a/examples/celix-examples/services_example_cxx/baz/CMakeLists.txt b/examples/celix-examples/svc_depependency_example_cxx/baz/CMakeLists.txt
similarity index 100%
rename from examples/celix-examples/services_example_cxx/baz/CMakeLists.txt
rename to examples/celix-examples/svc_depependency_example_cxx/baz/CMakeLists.txt
diff --git a/examples/celix-examples/services_example_cxx/baz/src/Baz.cc b/examples/celix-examples/svc_depependency_example_cxx/baz/src/Baz.cc
similarity index 100%
rename from examples/celix-examples/services_example_cxx/baz/src/Baz.cc
rename to examples/celix-examples/svc_depependency_example_cxx/baz/src/Baz.cc
diff --git a/examples/celix-examples/services_example_cxx/baz/src/Baz.h b/examples/celix-examples/svc_depependency_example_cxx/baz/src/Baz.h
similarity index 100%
rename from examples/celix-examples/services_example_cxx/baz/src/Baz.h
rename to examples/celix-examples/svc_depependency_example_cxx/baz/src/Baz.h
diff --git a/examples/celix-examples/services_example_cxx/baz/src/BazActivator.cc b/examples/celix-examples/svc_depependency_example_cxx/baz/src/BazActivator.cc
similarity index 95%
rename from examples/celix-examples/services_example_cxx/baz/src/BazActivator.cc
rename to examples/celix-examples/svc_depependency_example_cxx/baz/src/BazActivator.cc
index 9813885..f38c469 100644
--- a/examples/celix-examples/services_example_cxx/baz/src/BazActivator.cc
+++ b/examples/celix-examples/svc_depependency_example_cxx/baz/src/BazActivator.cc
@@ -19,7 +19,7 @@
 
 #include "Baz.h"
 #include "BazActivator.h"
-#include <celix_api.h>
+#include <celix/BundleActivator.h>
 
 using namespace celix::dm;
 
diff --git a/examples/celix-examples/services_example_cxx/baz/src/BazActivator.h b/examples/celix-examples/svc_depependency_example_cxx/baz/src/BazActivator.h
similarity index 100%
rename from examples/celix-examples/services_example_cxx/baz/src/BazActivator.h
rename to examples/celix-examples/svc_depependency_example_cxx/baz/src/BazActivator.h
diff --git a/examples/celix-examples/services_example_cxx/foo/CMakeLists.txt b/examples/celix-examples/svc_depependency_example_cxx/foo/CMakeLists.txt
similarity index 100%
rename from examples/celix-examples/services_example_cxx/foo/CMakeLists.txt
rename to examples/celix-examples/svc_depependency_example_cxx/foo/CMakeLists.txt
diff --git a/examples/celix-examples/services_example_cxx/foo/src/Foo.cc b/examples/celix-examples/svc_depependency_example_cxx/foo/src/Foo.cc
similarity index 100%
rename from examples/celix-examples/services_example_cxx/foo/src/Foo.cc
rename to examples/celix-examples/svc_depependency_example_cxx/foo/src/Foo.cc
diff --git a/examples/celix-examples/services_example_cxx/foo/src/Foo.h b/examples/celix-examples/svc_depependency_example_cxx/foo/src/Foo.h
similarity index 100%
rename from examples/celix-examples/services_example_cxx/foo/src/Foo.h
rename to examples/celix-examples/svc_depependency_example_cxx/foo/src/Foo.h
diff --git a/examples/celix-examples/services_example_cxx/foo/src/FooActivator.cc b/examples/celix-examples/svc_depependency_example_cxx/foo/src/FooActivator.cc
similarity index 94%
rename from examples/celix-examples/services_example_cxx/foo/src/FooActivator.cc
rename to examples/celix-examples/svc_depependency_example_cxx/foo/src/FooActivator.cc
index 50af21c..10dec88 100644
--- a/examples/celix-examples/services_example_cxx/foo/src/FooActivator.cc
+++ b/examples/celix-examples/svc_depependency_example_cxx/foo/src/FooActivator.cc
@@ -19,7 +19,7 @@
 
 #include "Foo.h"
 #include "FooActivator.h"
-#include <celix_api.h>
+#include <celix/BundleActivator.h>
 
 using namespace celix::dm;
 
diff --git a/examples/celix-examples/services_example_cxx/foo/src/FooActivator.h b/examples/celix-examples/svc_depependency_example_cxx/foo/src/FooActivator.h
similarity index 100%
rename from examples/celix-examples/services_example_cxx/foo/src/FooActivator.h
rename to examples/celix-examples/svc_depependency_example_cxx/foo/src/FooActivator.h
diff --git a/libs/framework/gtest/CMakeLists.txt b/libs/framework/gtest/CMakeLists.txt
index 89f9eb0..4e06772 100644
--- a/libs/framework/gtest/CMakeLists.txt
+++ b/libs/framework/gtest/CMakeLists.txt
@@ -19,6 +19,7 @@ add_celix_bundle(simple_test_bundle1 NO_ACTIVATOR VERSION 1.0.0)
 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)
+add_celix_bundle(simple_cxx_bundle SOURCES src/HelloWorldCxxActivator.cpp VERSION 1.0.0)
 add_subdirectory(subdir) #simple_test_bundle4, simple_test_bundle5 and sublib
 
 add_celix_bundle(unresolveable_bundle SOURCES src/nop_activator.c VERSION 1.0.0)
@@ -37,12 +38,17 @@ add_executable(test_framework
     src/bundle_context_bundles_tests.cpp
     src/bundle_context_services_test.cpp
     src/DependencyManagerTestSuite.cc
+    src/CxxBundleContext_tests.cpp
+    src/CxxProperties_tests.cpp
+    src/HelloWorldCxxActivator.cpp
+    src/CxxFilter_tests.cpp
 )
 
 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)
+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)
 target_include_directories(test_framework PRIVATE ../src)
 
+celix_get_bundle_file(simple_cxx_bundle SIMPLE_CXX_BUNDLE_LOC)
 target_compile_definitions(test_framework PRIVATE
         -DSIMPLE_TEST_BUNDLE1_LOCATION="$<TARGET_PROPERTY:simple_test_bundle1,BUNDLE_FILE>"
         -DSIMPLE_TEST_BUNDLE2_LOCATION="$<TARGET_PROPERTY:simple_test_bundle2,BUNDLE_FILE>"
@@ -51,6 +57,7 @@ target_compile_definitions(test_framework PRIVATE
         -DSIMPLE_TEST_BUNDLE5_LOCATION="$<TARGET_PROPERTY:simple_test_bundle5,BUNDLE_FILENAME>"
         -DTEST_BUNDLE_WITH_EXCEPTION_LOCATION="$<TARGET_PROPERTY:bundle_with_exception,BUNDLE_FILE>"
         -DTEST_BUNDLE_UNRESOLVEABLE_LOCATION="$<TARGET_PROPERTY:unresolveable_bundle,BUNDLE_FILE>"
+        -DSIMPLE_CXX_BUNDLE_LOC="${SIMPLE_CXX_BUNDLE_LOC}"
 )
 
 configure_file(config.properties.in config.properties @ONLY)
diff --git a/libs/framework/gtest/src/CxxBundleContext_tests.cpp b/libs/framework/gtest/src/CxxBundleContext_tests.cpp
new file mode 100644
index 0000000..60915b5
--- /dev/null
+++ b/libs/framework/gtest/src/CxxBundleContext_tests.cpp
@@ -0,0 +1,435 @@
+/*
+ * 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 <atomic>
+
+#include "celix/BundleContext.h"
+
+#include "celix_framework_factory.h"
+#include "celix_framework.h"
+
+class CxxBundleContextTestSuite : public ::testing::Test {
+public:
+    static constexpr const char * const TEST_BND1_LOC = "" SIMPLE_TEST_BUNDLE1_LOCATION "";
+    static constexpr const char * const TEST_BND2_LOC = "" SIMPLE_TEST_BUNDLE2_LOCATION "";
+
+    CxxBundleContextTestSuite() {
+        auto* properties = properties_create();
+        properties_set(properties, "LOGHELPER_ENABLE_STDOUT_FALLBACK", "true");
+        properties_set(properties, "org.osgi.framework.storage.clean", "onFirstInit");
+        properties_set(properties, "org.osgi.framework.storage", ".cacheCxxBundleContextTestFramework");
+
+        auto* cfw = celix_frameworkFactory_createFramework(properties);
+        fw = std::shared_ptr<celix_framework_t>{cfw, [](celix_framework_t* f){ celix_frameworkFactory_destroyFramework(f); }};
+    }
+
+    std::shared_ptr<celix_framework_t> fw{};
+};
+
+class TestInterface {
+
+};
+
+class TestImplementation : public TestInterface {
+
+};
+
+struct CInterface {
+    void *handle;
+    void (*hello)(void *handle);
+};
+
+TEST_F(CxxBundleContextTestSuite, RegisterServiceTest) {
+    auto* cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+    
+    long svcId = celix_bundleContext_findService(cCtx, celix::typeName<TestInterface>().c_str());
+    EXPECT_EQ(svcId, -1L);
+
+    {
+        auto impl = std::make_shared<TestImplementation>();
+        auto svcReg = ctx.registerService<TestInterface>(impl).build();
+        svcReg->wait();
+        svcId = celix_bundleContext_findService(cCtx, celix::typeName<TestInterface>().c_str());
+        EXPECT_GE(svcId, 0L);
+    }
+
+    celix_bundleContext_waitForEvents(cCtx);
+    svcId = celix_bundleContext_findService(cCtx, celix::typeName<TestInterface>().c_str());
+    EXPECT_EQ(svcId, -1L);
+}
+
+TEST_F(CxxBundleContextTestSuite, RegisterCServiceTest) {
+    auto *cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+
+    auto svc = std::make_shared<CInterface>(CInterface{nullptr, nullptr});
+    auto svcReg = ctx.registerService<CInterface>(svc).build();
+    svcReg->wait();
+
+    std::cout << "Name is " << celix::typeName<CInterface>() << std::endl;
+    long svcId = celix_bundleContext_findService(cCtx, celix::typeName<CInterface>().c_str());
+    EXPECT_GE(svcId, 0L);
+
+    svcReg->unregister();
+    svcId = celix_bundleContext_findService(cCtx, celix::typeName<CInterface>().c_str());
+    EXPECT_GE(svcId, -1);
+
+    CInterface svc2{nullptr, nullptr};
+    auto svcReg2 = ctx.registerUnmanagedService(&svc2).build();
+
+    celix_bundleContext_waitForEvents(cCtx);
+    svcId = celix_bundleContext_findService(cCtx, celix::typeName<CInterface>().c_str());
+    EXPECT_GE(svcId, 0L);
+
+    svcReg2->unregister();
+    celix_bundleContext_waitForEvents(cCtx);
+    svcId = celix_bundleContext_findService(cCtx, celix::typeName<CInterface>().c_str());
+    EXPECT_GE(svcId, -1);
+}
+
+TEST_F(CxxBundleContextTestSuite, UseServicesTest) {
+    auto *cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+
+    auto count = ctx.useService<CInterface>().build();
+    EXPECT_EQ(count, 0);
+
+    auto svc = std::make_shared<CInterface>(CInterface{nullptr, nullptr});
+    auto svcReg1 = ctx.registerService<CInterface>(svc).build();
+    auto svcReg2 = ctx.registerService<CInterface>(svc).build();
+
+    std::atomic<int> countFromFunction{0};
+    count = ctx.useService<CInterface>().addUseCallback([&countFromFunction](CInterface&){countFromFunction += 1;}).build();
+    EXPECT_EQ(count, 1);
+    EXPECT_EQ(countFromFunction.load(), 1);
+
+    countFromFunction = 0;
+    count = ctx.useServices<CInterface>()
+        .setTimeout(std::chrono::seconds{1})
+        .addUseCallback([&countFromFunction](CInterface&, const celix::Properties& props){
+            countFromFunction += 1;
+            auto id = props.getAsLong(OSGI_FRAMEWORK_SERVICE_ID, -1L);
+            EXPECT_GT(id, -1L);
+        })
+        .build();
+    count += ctx.useServices<CInterface>().addUseCallback([&countFromFunction](CInterface&, const celix::Properties& props, const celix::Bundle& bnd) {
+        countFromFunction += 1;
+        EXPECT_GE(props.getAsLong(OSGI_FRAMEWORK_SERVICE_ID, -1L), 0);
+        EXPECT_GE(bnd.getId(), -1L);
+    }).build();
+    EXPECT_EQ(count, 4);
+    EXPECT_EQ(countFromFunction.load(), 4);
+}
+
+TEST_F(CxxBundleContextTestSuite, FindServicesTest) {
+    auto *cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+
+    auto svcId = ctx.findService<CInterface>();
+    EXPECT_EQ(svcId, -1L);
+    auto svcIds = ctx.findServices<CInterface>();
+    EXPECT_EQ(svcIds.size(), 0);
+
+
+    celix::Properties props{};
+    props["key1"] = "value1";
+    auto svc = std::make_shared<CInterface>(CInterface{nullptr, nullptr});
+    auto svcReg1 = ctx.registerService<CInterface>(svc)
+            .setProperties(props)
+            .addProperty("key2", "value2")
+            .build();
+    svcReg1->wait();
+    auto svcReg2 = ctx.registerService<CInterface>(svc).build();
+    svcReg2->wait();
+
+    svcId = ctx.findService<CInterface>();
+    EXPECT_EQ(svcId, svcReg1->getServiceId()); //svcReg1 -> registered first
+
+    svcIds = ctx.findServices<CInterface>();
+    EXPECT_EQ(svcIds.size(), 2);
+    svcIds = ctx.findServices<CInterface>("(&(key1=value1)(key2=value2))");
+    EXPECT_EQ(svcIds.size(), 1); //only 1 svc matches the filter
+
+    svcReg1->unregister();
+    svcReg1->wait();
+    svcId = ctx.findService<CInterface>();
+    EXPECT_EQ(svcId, svcReg2->getServiceId()); //svcReg2 -> svcReg1 unregistered
+}
+
+
+TEST_F(CxxBundleContextTestSuite, TrackServicesTest) {
+    auto *cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+
+    auto tracker = ctx.trackServices<CInterface>().build();
+    tracker->wait();
+    EXPECT_TRUE(tracker->isOpen());
+    EXPECT_EQ(0, tracker->getServiceCount());
+
+    celix::Properties props{};
+    props["key1"] = "value1";
+    auto svc = std::make_shared<CInterface>(CInterface{nullptr, nullptr});
+    auto svcReg1 = ctx.registerService<CInterface>(svc)
+            .setProperties(props)
+            .addProperty("key2", "value2")
+            .build();
+    svcReg1->wait();
+    auto svcReg2 = ctx.registerService<CInterface>(svc).build();
+    svcReg2->wait();
+    EXPECT_EQ(2, tracker->getServiceCount());
+
+    auto tracker2 = ctx.trackServices<CInterface>().setFilter("(key1=value1)").build();
+    tracker2->wait();
+    EXPECT_EQ(1, tracker2->getServiceCount());
+
+    std::atomic<int> count{0};
+    auto tracker3 = ctx.trackServices<CInterface>()
+            .addAddCallback([&count](const std::shared_ptr<CInterface>&) {
+                count += 1;
+            })
+            .addRemCallback([&count](const std::shared_ptr<CInterface>&) {
+                count += 1;
+            })
+            .build();
+    tracker3->wait();
+    EXPECT_EQ(2, count); //2x add called
+    svcReg1->unregister();
+    svcReg1->wait();
+    EXPECT_EQ(3, count); //2x add called, 1x rem called
+    tracker3->close();
+    tracker3->wait();
+    EXPECT_EQ(4, count); //2x add called, 2x rem called (1 rem call for closing the tracker)
+
+    EXPECT_EQ(1, tracker->getServiceCount()); //only 1 left
+
+}
+
+TEST_F(CxxBundleContextTestSuite, TrackBundlesTest) {
+    auto *cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+
+    std::atomic<int> count{0};
+    auto cb = [&count](const celix::Bundle& bnd) {
+        EXPECT_GE(bnd.getId(), 0);
+        count++;
+    };
+
+    auto tracker = ctx.trackBundles()
+            .addOnInstallCallback(cb)
+            .addOnStartCallback(cb)
+            .addOnStopCallback(cb)
+            .build();
+    tracker->wait();
+    EXPECT_EQ(celix::TrackerState::OPEN, tracker->getCurrentState());
+    EXPECT_TRUE(tracker->isOpen());
+    EXPECT_EQ(0, count.load());
+
+    long bndId = ctx.installBundle("non-existing");
+    EXPECT_EQ(-1, bndId); //not installed
+
+    long bndId1 = ctx.installBundle(TEST_BND1_LOC);
+    EXPECT_GE(bndId1, 0);
+    EXPECT_EQ(2, count.load()); // 1x install, 1x start
+
+    long bndId2 = ctx.installBundle(TEST_BND2_LOC, false);
+    EXPECT_GE(bndId1, 0);
+    EXPECT_EQ(3, count.load()); // 2x install, 1x start
+    ctx.startBundle(bndId2);
+    EXPECT_EQ(4, count.load()); // 2x install, 2x start
+
+    ctx.uninstallBundle(bndId1);
+    EXPECT_EQ(5, count.load()); // 2x install, 2x start, 1x stop
+
+    ctx.stopBundle(bndId2);
+    EXPECT_EQ(6, count.load()); // 2x install, 2x start, 2x stop
+    ctx.startBundle(bndId2);
+    EXPECT_EQ(7, count.load()); // 2x install, 3x start, 2x stop
+}
+
+TEST_F(CxxBundleContextTestSuite, OnRegisterAndUnregisterCallbacks) {
+    auto *cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+
+    std::atomic<int> count{};
+    auto callback1 = [&count](const std::shared_ptr<celix::ServiceRegistration>& reg) {
+        EXPECT_EQ(celix::ServiceRegistrationState::REGISTERED, reg->getCurrentState());
+        count++;
+    };
+    auto callback2 = [&count](const std::shared_ptr<celix::ServiceRegistration>& reg) {
+        EXPECT_EQ(celix::ServiceRegistrationState::UNREGISTERED, reg->getCurrentState());
+        count--;
+    };
+
+    auto svcReg = ctx.registerService<TestInterface>(std::make_shared<TestImplementation>())
+            .setVersion("1.0.0")
+            .addOnRegistered(callback1)
+            .addOnRegistered(callback1)
+            .addOnRegistered([](const std::shared_ptr<celix::ServiceRegistration>& reg) {
+                std::cout << "Done registering service '" << reg->getServiceName() << "' with id " << reg->getServiceId() << std::endl;
+            })
+            .addOnUnregistered(callback2)
+            .addOnUnregistered(callback2)
+            .build();
+    svcReg->wait();
+    EXPECT_EQ(count.load(), 2); //2x incr callback1
+    svcReg->unregister();
+    svcReg->wait();
+    EXPECT_EQ(count.load(), 0); //2x descr callback2
+}
+
+TEST_F(CxxBundleContextTestSuite, InstallCxxBundle) {
+    auto *cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+
+    std::string loc{SIMPLE_CXX_BUNDLE_LOC};
+    ASSERT_FALSE(loc.empty());
+    long bndId = ctx.installBundle(loc);
+    EXPECT_GE(bndId, 0);
+}
+
+TEST_F(CxxBundleContextTestSuite, LoggingUsingContext) {
+    auto *cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+    ctx.logTrace("trace");
+    ctx.logDebug("debug");
+    ctx.logInfo("info");
+    ctx.logWarn("warn");
+    ctx.logError("error");
+    ctx.logFatal("fatal");
+
+}
+
+TEST_F(CxxBundleContextTestSuite, GetFramework) {
+    auto *cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+
+    EXPECT_FALSE(ctx.getFramework()->getUUID().empty());
+    EXPECT_EQ(ctx.getFramework()->getFrameworkBundleContext()->getBundle().getId(), 0);
+}
+
+TEST_F(CxxBundleContextTestSuite, GetConfigurations) {
+    auto *cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+
+    EXPECT_EQ(ctx.getConfigProperty("non-existing", "test"), std::string{"test"});
+    EXPECT_EQ(ctx.getConfigPropertyAsLong("non-existing", -1L), -1L);
+    EXPECT_EQ(ctx.getConfigPropertyAsDouble("non-existing", 0), 0);
+    EXPECT_EQ(ctx.getConfigPropertyAsBool("non-existing", true), true);
+
+}
+
+TEST_F(CxxBundleContextTestSuite, TrackAnyServices) {
+    auto *cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+
+    auto svc1 = std::make_shared<CInterface>(CInterface{nullptr, nullptr});
+    auto svcReg1 = ctx.registerService<CInterface>(svc1).build();
+    svcReg1->wait();
+
+    auto svc2 = std::make_shared<TestImplementation>();
+    auto svcReg2 = ctx.registerService<TestInterface>(svc2).build();
+    svcReg2->wait();
+
+    auto trk = ctx.trackAnyServices().build();
+    trk->wait();
+    EXPECT_EQ(2, trk->getServiceCount());
+}
+
+TEST_F(CxxBundleContextTestSuite, TrackServiceTracker) {
+    auto *cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+
+    std::atomic<int> count{0};
+
+    auto metaTracker = ctx.trackServiceTrackers<TestInterface>()
+            .addOnTrackerCreatedCallback([&count](const celix::ServiceTrackerInfo& info) {
+                EXPECT_EQ(celix::typeName<TestInterface>(), info.serviceName);
+                count++;
+            })
+            .addOnTrackerDestroyedCallback([&count](const celix::ServiceTrackerInfo& info) {
+                EXPECT_EQ(celix::typeName<TestInterface>(), info.serviceName);
+                count--;
+            })
+            .build();
+    metaTracker->wait();
+    EXPECT_EQ(0, count.load()); // 0x created, 0x destroyed
+
+    auto trk1 = ctx.trackServices<TestInterface>().build();
+    auto trk2 = ctx.trackServices<TestInterface>().build();
+    auto trk3 = ctx.trackAnyServices().build(); //should not trigger callbacks
+    auto trk4 = ctx.trackServices<CInterface>().build(); //should not trigger callbacks
+    trk1->wait();
+    trk2->wait();
+    trk3->wait();
+    trk4->wait();
+    EXPECT_EQ(2, count.load()); // 2x created, 0x destroyed
+
+    trk1->close();
+    trk2->close();
+    trk3->close();
+    trk4->close();
+    trk1->wait();
+    trk2->wait();
+    trk3->wait();
+    trk4->wait();
+    EXPECT_EQ(0, count.load()); // 2x created, 2x destroyed
+}
+
+TEST_F(CxxBundleContextTestSuite, TrackAnyServiceTracker) {
+    auto *cCtx = celix_framework_getFrameworkContext(fw.get());
+    celix::BundleContext ctx{cCtx};
+
+    std::atomic<int> count{0};
+
+    auto metaTracker = ctx.trackAnyServiceTrackers()
+            .addOnTrackerCreatedCallback([&count](const celix::ServiceTrackerInfo& info) {
+                EXPECT_FALSE(info.filter.getFilterString().empty());
+                count++;
+            })
+            .addOnTrackerDestroyedCallback([&count](const celix::ServiceTrackerInfo& info) {
+                EXPECT_FALSE(info.filter.getFilterString().empty());
+                count--;
+            })
+            .build();
+    metaTracker->wait();
+    EXPECT_EQ(0, count.load()); // 0x created, 0x destroyed
+
+    auto trk1 = ctx.trackServices<TestInterface>().build();
+    auto trk2 = ctx.trackServices<TestInterface>().build();
+    auto trk3 = ctx.trackAnyServices().build();
+    auto trk4 = ctx.trackServices<CInterface>().build();
+    trk1->wait();
+    trk2->wait();
+    trk3->wait();
+    trk4->wait();
+    EXPECT_EQ(4, count.load()); // 4x created, 0x destroyed
+
+    trk1->close();
+    trk2->close();
+    trk3->close();
+    trk4->close();
+    trk1->wait();
+    trk2->wait();
+    trk3->wait();
+    trk4->wait();
+    EXPECT_EQ(0, count.load()); // 4x created, 4x destroyed
+}
\ No newline at end of file
diff --git a/libs/framework/gtest/src/CxxFilter_tests.cpp b/libs/framework/gtest/src/CxxFilter_tests.cpp
new file mode 100644
index 0000000..f22ac9d
--- /dev/null
+++ b/libs/framework/gtest/src/CxxFilter_tests.cpp
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "celix/Filter.h"
+
+class CxxFilterTestSuite : public ::testing::Test {
+public:
+};
+
+
+TEST_F(CxxFilterTestSuite, CreateDestroy) {
+    //TODO support empty filter
+//    celix::Filter filter{};
+//    EXPECT_TRUE(filter.getCFilter() != nullptr);
+}
+
+TEST_F(CxxFilterTestSuite, FilterString) {
+    //TODO
+//    celix::Filter filter1{};
+//    EXPECT_EQ(std::string{}, filter1.getFilterString());
+
+    celix::Filter filter2{"(key=value)"};
+    EXPECT_EQ(std::string{"(key=value)"}, filter2.getFilterString());
+}
+
+TEST_F(CxxFilterTestSuite, EmptyFilterTest) {
+    //TODO match test that match is always true
+}
+
+TEST_F(CxxFilterTestSuite, MatchTest) {
+    celix::Filter filter1{"(key=value)"};
+    celix::Filter filter2{"(key=value2)"};
+    celix::Properties props1{};
+    props1.set("key", "value");
+    celix::Properties props2{};
+
+    EXPECT_TRUE(filter1.match(props1));
+    EXPECT_FALSE(filter2.match(props1));
+    EXPECT_FALSE(filter1.match(props2));
+    EXPECT_FALSE(filter2.match(props2));
+}
+
+TEST_F(CxxFilterTestSuite, FindAttributes) {
+    celix::Filter filter1{"(&(key1=value1)(key2=value2)(|(key3=value3)(key4=*)))"};
+
+    EXPECT_TRUE(filter1.hasAttribute("key1"));
+    EXPECT_TRUE(filter1.hasAttribute("key2"));
+    EXPECT_TRUE(filter1.hasAttribute("key3"));
+    EXPECT_TRUE(filter1.hasAttribute("key4"));
+    EXPECT_FALSE(filter1.hasAttribute("key"));
+
+    EXPECT_EQ(filter1.findAttribute("key1"), std::string{"value1"});
+    EXPECT_EQ(filter1.findAttribute("key2"), std::string{"value2"});
+    EXPECT_EQ(filter1.findAttribute("key3"), std::string{"value3"});
+    EXPECT_EQ(filter1.findAttribute("key4"), std::string{"*"});
+    EXPECT_TRUE(filter1.findAttribute("key").empty());
+}
\ No newline at end of file
diff --git a/libs/framework/gtest/src/CxxProperties_tests.cpp b/libs/framework/gtest/src/CxxProperties_tests.cpp
new file mode 100644
index 0000000..1c6651e
--- /dev/null
+++ b/libs/framework/gtest/src/CxxProperties_tests.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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/Properties.h"
+
+using ::testing::MatchesRegex;
+
+class CxxPropertiesTestSuite : public ::testing::Test {
+public:
+};
+
+TEST_F(CxxPropertiesTestSuite, CreateDestroy) {
+    celix::Properties props{};
+    EXPECT_EQ(0, props.size());
+}
+
+TEST_F(CxxPropertiesTestSuite, FillAndLoop) {
+    celix::Properties props{};
+    EXPECT_EQ(0, props.size());
+
+    props["key1"] = "value1";
+    props.set("key2", "value2");
+    props.set("key3", 3.3);
+    props.set("key4", 4);
+    props.set("key5", true);
+    EXPECT_EQ(5, props.size());
+
+    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);
+    EXPECT_EQ(props.get("key5"), "true");
+    EXPECT_EQ(props.getAsBool("key5", false), true);
+
+    int count = 0;
+    for (const auto& pair : props) {
+        EXPECT_NE(pair.first, "");
+        count++;
+    }
+    EXPECT_EQ(5, count);
+}
+
+TEST_F(CxxPropertiesTestSuite, CopyTest) {
+    celix::Properties props{};
+
+    props["key1"] = "value1";
+    props["key2"] = "value2";
+
+    celix::Properties copy = props;
+    copy["key1"] = "value1_new";
+
+    std::string v1 = props["key1"];
+    std::string v2 =  copy["key1"];
+    EXPECT_EQ(v1, "value1");
+    EXPECT_EQ(v2, "value1_new");
+}
+
+TEST_F(CxxPropertiesTestSuite, WrapTest) {
+    auto *props = celix_properties_create();
+    celix_properties_set(props, "test", "test");
+
+    EXPECT_EQ(1, celix_properties_size(props));
+    {
+        auto cxxProps = celix::Properties::wrap(props);
+        EXPECT_EQ(1, cxxProps->size());
+        EXPECT_EQ(props, cxxProps->getCProperties());
+    } //NOTE cxxProps out of scope, but will not destroy celix_properties
+    EXPECT_EQ(1, celix_properties_size(props));
+
+    celix_properties_destroy(props);
+}
diff --git a/libs/framework/gtest/src/DependencyManagerTestSuite.cc b/libs/framework/gtest/src/DependencyManagerTestSuite.cc
index 419eb61..c4c00b8 100644
--- a/libs/framework/gtest/src/DependencyManagerTestSuite.cc
+++ b/libs/framework/gtest/src/DependencyManagerTestSuite.cc
@@ -20,7 +20,8 @@
 #include <gtest/gtest.h>
 #include <atomic>
 
-#include "celix_api.h"
+#include "celix/dm/DependencyManager.h"
+#include "celix_framework_factory.h"
 
 class DependencyManagerTestSuite : public ::testing::Test {
 public:
diff --git a/libs/framework/include/celix/dm/Properties.h b/libs/framework/gtest/src/HelloWorldCxxActivator.cpp
similarity index 73%
copy from libs/framework/include/celix/dm/Properties.h
copy to libs/framework/gtest/src/HelloWorldCxxActivator.cpp
index 5c33885..c7df45d 100644
--- a/libs/framework/include/celix/dm/Properties.h
+++ b/libs/framework/gtest/src/HelloWorldCxxActivator.cpp
@@ -17,11 +17,15 @@
  * under the License.
  */
 
-#pragma once
+#include "celix/BundleActivator.h"
 
-#include <map>
-#include <string>
+namespace {
+    class BundleActivator {
+    public:
+        explicit BundleActivator(const std::shared_ptr<celix::BundleContext>& ctx) {
+            ctx->logInfo("Cxx Bundle Started");
+        }
+    };
+}
 
-namespace celix { namespace dm {
-    using Properties = std::map<std::string, std::string>;
-}}
+CELIX_GEN_CXX_BUNDLE_ACTIVATOR(BundleActivator)
\ No newline at end of file
diff --git a/libs/framework/gtest/src/bundle_context_services_test.cpp b/libs/framework/gtest/src/bundle_context_services_test.cpp
index b19fbe9..87345f5 100644
--- a/libs/framework/gtest/src/bundle_context_services_test.cpp
+++ b/libs/framework/gtest/src/bundle_context_services_test.cpp
@@ -92,7 +92,7 @@ TEST_F(CelixBundleContextServicesTests, registerServiceAsync) {
     celix_bundleContext_waitForAsyncRegistration(ctx, svcId);
     ASSERT_GE(celix_bundleContext_findService(ctx, calcName), 0L);
 
-    celix_bundleContext_unregisterServiceAsync(ctx, svcId, NULL, NULL);
+    celix_bundleContext_unregisterServiceAsync(ctx, svcId, nullptr, nullptr);
     celix_bundleContext_waitForAsyncUnregistration(ctx, svcId);
     ASSERT_LT(celix_bundleContext_findService(ctx, calcName), 0L);
 };
@@ -105,10 +105,10 @@ TEST_F(CelixBundleContextServicesTests, incorrectUnregisterCalls) {
 };
 
 TEST_F(CelixBundleContextServicesTests, incorrectAsyncUnregisterCalls) {
-    celix_bundleContext_unregisterServiceAsync(ctx, 1, NULL, NULL);
-    celix_bundleContext_unregisterServiceAsync(ctx, 2, NULL, NULL);
-    celix_bundleContext_unregisterServiceAsync(ctx, -1, NULL, NULL);
-    celix_bundleContext_unregisterServiceAsync(ctx, -2, NULL, NULL);
+    celix_bundleContext_unregisterServiceAsync(ctx, 1, nullptr, nullptr);
+    celix_bundleContext_unregisterServiceAsync(ctx, 2, nullptr, nullptr);
+    celix_bundleContext_unregisterServiceAsync(ctx, -1, nullptr, nullptr);
+    celix_bundleContext_unregisterServiceAsync(ctx, -2, nullptr, nullptr);
 };
 
 TEST_F(CelixBundleContextServicesTests, registerMultipleAndUseServices) {
@@ -286,7 +286,7 @@ TEST_F(CelixBundleContextServicesTests, registerAsyncAndUseServiceWithTimeout) {
         EXPECT_TRUE(result.get()); //should return true after waiting for the registered service.
 
 
-        celix_bundleContext_unregisterServiceAsync(ctx, svcId, NULL, NULL);
+        celix_bundleContext_unregisterServiceAsync(ctx, svcId, nullptr, nullptr);
         celix_bundleContext_waitForAsyncUnregistration(ctx, svcId);
 
 
@@ -516,7 +516,7 @@ TEST_F(CelixBundleContextServicesTests, servicesTrackerTestAsync) {
     ASSERT_TRUE(svcId2 >= 0);
     ASSERT_EQ(2, count);
 
-    celix_bundleContext_unregisterServiceAsync(ctx, svcId1, NULL, NULL);
+    celix_bundleContext_unregisterServiceAsync(ctx, svcId1, nullptr, nullptr);
     celix_bundleContext_waitForAsyncUnregistration(ctx, svcId1);
     ASSERT_EQ(1, count);
 
@@ -593,13 +593,13 @@ TEST_F(CelixBundleContextServicesTests, servicesTrackerTestWithProperties) {
     int count = 0;
     auto add = [](void *handle, void *svc, const properties_t *props) {
         ASSERT_TRUE(svc != nullptr);
-        ASSERT_STRCASEEQ("C", celix_properties_get(props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, nullptr));
+        ASSERT_TRUE(props != nullptr);
         int *c = static_cast<int*>(handle);
         *c += 1;
     };
     auto remove = [](void *handle, void *svc, const properties_t *props) {
         ASSERT_TRUE(svc != nullptr);
-        ASSERT_STRCASEEQ("C", celix_properties_get(props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, nullptr));
+        ASSERT_TRUE(props != nullptr);
         int *c = static_cast<int*>(handle);
         *c -= 1;
     };
@@ -630,14 +630,14 @@ TEST_F(CelixBundleContextServicesTests, servicesTrackerTestWithOwner) {
     int count = 0;
     auto add = [](void *handle, void *svc, const properties_t *props, const bundle_t *svcOwner) {
         ASSERT_TRUE(svc != nullptr);
-        ASSERT_STRCASEEQ("C", celix_properties_get(props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, nullptr));
+        ASSERT_TRUE(props != nullptr);
         ASSERT_TRUE(celix_bundle_getId(svcOwner) >= 0);
         int *c = static_cast<int*>(handle);
         *c += 1;
     };
     auto remove = [](void *handle, void *svc, const properties_t *props, const bundle_t *svcOwner) {
         ASSERT_TRUE(svc != nullptr);
-        ASSERT_STRCASEEQ("C", celix_properties_get(props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, nullptr));
+        ASSERT_TRUE(props != nullptr);
         ASSERT_TRUE(celix_bundle_getId(svcOwner) >= 0);
         int *c = static_cast<int*>(handle);
         *c -= 1;
@@ -818,7 +818,7 @@ TEST_F(CelixBundleContextServicesTests, servicesTrackerSetTest) {
     //unregister svc3 should lead to set (new highest ranking)
     celix_bundleContext_unregisterService(ctx, svcId3); //call 3
 
-    celix_bundleContext_stopTracker(ctx, trackerId); //call 4 (NULL)
+    celix_bundleContext_stopTracker(ctx, trackerId); //call 4 (nullptr)
     celix_bundleContext_unregisterService(ctx, svcId1);
     celix_bundleContext_unregisterService(ctx, svcId2);
     celix_bundleContext_unregisterService(ctx, svcId4);
@@ -962,7 +962,7 @@ TEST_F(CelixBundleContextServicesTests, asyncServiceFactoryTest) {
     ASSERT_EQ(2, count); //expecting getService & unGetService to be called during the useService call.
 
 
-    celix_bundleContext_unregisterServiceAsync(ctx, facId, NULL, NULL);
+    celix_bundleContext_unregisterServiceAsync(ctx, facId, nullptr, nullptr);
 }
 
 TEST_F(CelixBundleContextServicesTests, findServicesTest) {
@@ -1004,13 +1004,11 @@ TEST_F(CelixBundleContextServicesTests, trackServiceTrackerTest) {
 
     auto add = [](void *handle, const celix_service_tracker_info_t *info) {
         EXPECT_STRCASEEQ("example", info->serviceName);
-        EXPECT_STRCASEEQ(CELIX_FRAMEWORK_SERVICE_C_LANGUAGE, info->serviceLanguage);
         auto *c = static_cast<int*>(handle);
         *c += 1;
     };
     auto remove = [](void *handle, const celix_service_tracker_info_t *info) {
         EXPECT_STRCASEEQ("example", info->serviceName);
-        EXPECT_STRCASEEQ(CELIX_FRAMEWORK_SERVICE_C_LANGUAGE, info->serviceLanguage);
         auto *c = static_cast<int*>(handle);
         *c -= 1;
     };
diff --git a/libs/framework/include/celix/Builders.h b/libs/framework/include/celix/Builders.h
new file mode 100644
index 0000000..795ce05
--- /dev/null
+++ b/libs/framework/include/celix/Builders.h
@@ -0,0 +1,600 @@
+/*
+ * 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 <memory>
+#include <functional>
+
+#include "celix/ServiceRegistration.h"
+#include "celix/Trackers.h"
+
+namespace celix {
+
+    /**
+     * Fluent builder API to build a new service registration for a service of type I.
+     *
+     * \warning Not thread safe.
+     * @tparam I The service type (Note should be the abstract interface, not the interface implementer)
+     */
+    template<typename I>
+    class ServiceRegistrationBuilder {
+    private:
+        friend class BundleContext;
+
+        //NOTE private to prevent move so that a build() call cannot be forgotten
+        ServiceRegistrationBuilder(ServiceRegistrationBuilder&&) = default;
+    public:
+        ServiceRegistrationBuilder(std::shared_ptr<celix_bundle_context_t> _cCtx, std::shared_ptr<I> _svc, std::string _name) :
+                cCtx{std::move(_cCtx)},
+                svc{std::move(_svc)},
+                name{std::move(_name)} {
+
+        }
+
+        ServiceRegistrationBuilder& operator=(ServiceRegistrationBuilder&&) = delete;
+        ServiceRegistrationBuilder(const ServiceRegistrationBuilder&) = delete;
+        ServiceRegistrationBuilder operator=(const ServiceRegistrationBuilder&) = delete;
+
+        /**
+         * Set the service version.
+         * This will lead to a 'service.version' service property.
+         */
+        ServiceRegistrationBuilder& setVersion(std::string v) { version = std::move(v); return *this; }
+
+        /**
+         * Add a property to the service properties.
+         * 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; }
+
+        /**
+         * 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; }
+
+        /**
+         * Add a property to the service properties.
+         * 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; }
+
+        /**
+         * Set the service properties.
+         * Note this call will clear any already set properties.
+         */
+        ServiceRegistrationBuilder& setProperties(celix::Properties p) { properties = std::move(p); return *this; }
+
+        /**
+         * Add the properties to the service properties.
+         * Note this call will add these properties to the already set properties.
+         * If a key is already present the value will be overridden.
+         */
+        ServiceRegistrationBuilder& addProperties(const celix::Properties& props) {
+            for (const auto& pair : props) {
+                properties.set(pair.first, pair.second);
+            }
+            return *this;
+        }
+
+        /**
+         * Adds an on registered callback for the service registration.
+         * This callback will be called on the Celix event thread when the service is registered (REGISTERED state)
+         */
+        ServiceRegistrationBuilder& addOnRegistered(std::function<void(const std::shared_ptr<ServiceRegistration>&)> callback) {
+            onRegisteredCallbacks.emplace_back(std::move(callback));
+            return *this;
+        }
+
+        /**
+         * Adds an on unregistered callback for the service registration.
+         * This callback will be called on the Celix event thread when the service is unregistered (UNREGISTERED state)
+         */
+        ServiceRegistrationBuilder& addOnUnregistered(std::function<void(const std::shared_ptr<ServiceRegistration>&)> callback) {
+            onUnregisteredCallbacks.emplace_back(std::move(callback));
+            return *this;
+        }
+
+        /**
+         * "Builds" the service registration and return a ServiceRegistration.
+         *
+         * The ServiceRegistration will async register the service to the Celix framwork and unregister the service if
+         * the ServiceRegistration is destroyed.
+         *
+         */
+        std::shared_ptr<ServiceRegistration> build() {
+            auto result = std::make_shared<ServiceRegistration>(cCtx, std::move(svc), std::move(name), std::move(version), std::move(properties), std::move(onRegisteredCallbacks), std::move(onUnregisteredCallbacks));
+            result->setSelf(result);
+            //TODO after moves , builder is not valid anymore. TBD howto handle this
+            return result;
+        }
+    private:
+        const std::shared_ptr<celix_bundle_context_t> cCtx;
+        std::shared_ptr<I> svc;
+        std::string name;
+        std::string version{};
+        celix::Properties properties{};
+        std::vector<std::function<void(const std::shared_ptr<ServiceRegistration>&)>> onRegisteredCallbacks{};
+        std::vector<std::function<void(const std::shared_ptr<ServiceRegistration>&)>> onUnregisteredCallbacks{};
+    };
+
+    /**
+     * Fluent builder API to use a service(s) of type I.
+     *
+     * \warning Not thread safe.
+     * @tparam I The service type to use
+     */
+    template<typename I>
+    class UseServiceBuilder {
+    private:
+        friend class BundleContext;
+
+        //NOTE private to prevent move so that a build() call cannot be forgotten
+        UseServiceBuilder(UseServiceBuilder&&) = default;
+    public:
+        explicit UseServiceBuilder(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string _name, bool _useSingleService = true) :
+                cCtx{std::move(_cCtx)},
+                name{std::move(_name)},
+                useSingleService{_useSingleService} {
+        }
+
+        UseServiceBuilder& operator=(UseServiceBuilder&&) = delete;
+        UseServiceBuilder(const UseServiceBuilder&) = delete;
+        UseServiceBuilder operator=(const UseServiceBuilder&) = delete;
+
+        /**
+         * Set filter to be used to select a service.
+         * The filter must be LDAP filter.
+         * Example: "(property_key=value)"
+         */
+        UseServiceBuilder& setFilter(std::string f) { filter = std::move(f); return *this; }
+
+        /**
+         * Sets a optional timeout.
+         * If the timeout is > 0 and there is no matching service, the "build" will block
+         * until a matching service is found or the timeout is expired.
+         *
+         * Note: timeout is only valid if the a single service is used.
+         *
+         */
+        template <typename Rep, typename Period>
+        UseServiceBuilder& setTimeout(std::chrono::duration<Rep, Period> duration) {
+            auto micro =  std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
+            timeoutInSeconds = micro / 1000000;
+            return *this;
+        }
+
+        /**
+         * Adds a use callback function which will be called when the UseServiceBuilder is
+         * "build".
+         *
+         * The use callback function has 1 argument: a reference to the matching service.
+         */
+        UseServiceBuilder& addUseCallback(std::function<void(I&)> cb) {
+            callbacks.emplace_back(std::move(cb));
+            return *this;
+        }
+
+        /**
+         * Adds a use callback function which will be called when the UseServiceBuilder is
+         * "build".
+         *
+         * The use callback function has 2 arguments:
+         *  - A reference to the matching service.
+         *  - A const reference to the matching service properties.
+         */
+        UseServiceBuilder& addUseCallback(std::function<void(I&, const celix::Properties&)> cb) {
+            callbacksWithProperties.emplace_back(std::move(cb));
+            return *this;
+        }
+
+        /**
+         * Adds a use callback function which will be called when the UseServiceBuilder is
+         * "build".
+         *
+         * The use callback function has 3 arguments:
+         *  - A reference to the matching service.
+         *  - A const reference to the matching service properties.
+         *  - A const reference to the bundle owning the matching service.
+         */
+        UseServiceBuilder& addUseCallback(std::function<void(I&, const celix::Properties&, const celix::Bundle&)> cb) {
+            callbacksWithOwner.emplace_back(std::move(cb));
+            return *this;
+        }
+
+        /**
+         * "Builds" the UseServiceBuild and returns directly if no matching service is found
+         * or blocks until:
+         *  - All the use callback functions are called with the highest ranking matching service
+         *  - The timeout has expired
+         *
+         * \returns the number of service called (1 or 0).
+         */
+        std::size_t build() {
+            celix_service_use_options_t opts{};
+            opts.filter.serviceName = name.empty() ? nullptr : name.c_str();
+            opts.filter.ignoreServiceLanguage = true;
+            opts.filter.filter = filter.empty() ? nullptr : filter.c_str();
+            opts.filter.versionRange = versionRange.empty() ? nullptr : versionRange.c_str();
+            opts.waitTimeoutInSeconds = timeoutInSeconds;
+            opts.callbackHandle = this;
+            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)};
+                auto props = celix::Properties::wrap(cProps);
+                for (const auto& func : builder->callbacks) {
+                    func(*svc);
+                }
+                for (const auto& func : builder->callbacksWithProperties) {
+                    func(*svc, *props);
+                }
+                for (const auto& func : builder->callbacksWithOwner) {
+                    func(*svc, *props, bnd);
+                }
+            };
+
+            if (useSingleService) {
+                bool called = celix_bundleContext_useServiceWithOptions(cCtx.get(), &opts);
+                return called ? 1 : 0;
+            } else {
+                return celix_bundleContext_useServicesWithOptions(cCtx.get(), &opts);
+            }
+        }
+    private:
+        const std::shared_ptr<celix_bundle_context_t> cCtx;
+        const std::string name;
+        const bool useSingleService;
+        double timeoutInSeconds{0};
+        std::string filter{};
+        std::string versionRange{};
+        std::vector<std::function<void(I&)>> callbacks{};
+        std::vector<std::function<void(I&, const celix::Properties&)>> callbacksWithProperties{};
+        std::vector<std::function<void(I&, const celix::Properties&, const celix::Bundle&)>> callbacksWithOwner{};
+    };
+
+    /**
+     * Fluent builder API to track services of type I.
+     *
+     * \warning Not thread safe.
+     * @tparam I
+     */
+    template<typename I>
+    class ServiceTrackerBuilder {
+    private:
+        friend class BundleContext;
+
+        //NOTE private to prevent move so that a build() call cannot be forgotten
+        ServiceTrackerBuilder(ServiceTrackerBuilder&&) = default;
+    public:
+        explicit ServiceTrackerBuilder(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string _name) :
+                cCtx{std::move(_cCtx)},
+                name{std::move(_name)} {}
+
+        ServiceTrackerBuilder& operator=(ServiceTrackerBuilder&&) = delete;
+        ServiceTrackerBuilder(const ServiceTrackerBuilder&) = delete;
+        ServiceTrackerBuilder operator=(const ServiceTrackerBuilder&) = delete;
+
+        /**
+         * Set filter to be used to matching services.
+         * The filter must be LDAP filter.
+         * Example: "(property_key=value)"
+         */
+        ServiceTrackerBuilder& setFilter(std::string f) { filter = std::move(f); return *this; }
+
+        /**
+         * Adds a add callback function, which will be called - on the Celix event thread -
+         * when a new service match is found.
+         *
+         * The add callback function has 1 argument: A shared ptr to the added service.
+         */
+        ServiceTrackerBuilder& addAddCallback(std::function<void(std::shared_ptr<I>)> add) {
+            addCallbacks.emplace_back([add](std::shared_ptr<I> svc, std::shared_ptr<const celix::Properties>, const celix::Bundle&) {
+                add(svc);
+            });
+            return *this;
+        }
+
+        /**
+         * Adds a add callback function, which will be called - on the Celix event thread -
+         * when a new service match is found.
+         *
+         * The add callback function has 2 arguments:
+         *  - A shared ptr to the added service.
+         *  - A shared ptr to the added service properties.
+         */
+        ServiceTrackerBuilder& addAddWithPropertiesCallback(std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>)> add) {
+            addCallbacks.emplace_back([add](std::shared_ptr<I> svc, std::shared_ptr<const celix::Properties> props, const celix::Bundle&) {
+                add(svc, props);
+            });
+            return *this;
+        }
+
+        /**
+         * Adds a add callback function, which will be called - on the Celix event thread -
+         * when a new service match is found.
+         *
+         * The add callback function has 3 arguments:
+         *  - A shared ptr to the added service.
+         *  - A shared ptr to the added service properties.
+         *  - A shared ptr to the bundle owning the added service.
+         */
+        ServiceTrackerBuilder& addAddWithOwnerCallback(std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, const celix::Bundle&)> add) {
+            addCallbacks.emplace_back([add](std::shared_ptr<I> svc, std::shared_ptr<const celix::Properties> props, const celix::Bundle& bnd) {
+                add(svc, props, bnd);
+            });
+            return *this;
+        }
+
+        /**
+         * Adds a remove callback function, which will be called - on the Celix event thread -
+         * when a service match is being removed.
+         *
+         * The remove callback function has 1 arguments: A shared ptr to the removing service.
+         */
+        ServiceTrackerBuilder& addRemCallback(std::function<void(std::shared_ptr<I>)> remove) {
+            remCallbacks.emplace_back([remove](std::shared_ptr<I> svc, std::shared_ptr<const celix::Properties>, const celix::Bundle&) {
+                remove(svc);
+            });
+            return *this;
+        }
+
+        /**
+         * Adds a remove callback function, which will be called - on the Celix event thread -
+         * when a service match is being removed.
+         *
+         * The remove callback function has 2 arguments:
+         *  - A shared ptr to the removing service.
+         *  - A shared ptr to the removing service properties.
+         */
+        ServiceTrackerBuilder& addRemWithPropertiesCallback(std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>)> remove) {
+            remCallbacks.emplace_back([remove](std::shared_ptr<I> svc, std::shared_ptr<const celix::Properties> props, const celix::Bundle&) {
+                remove(svc, props);
+            });
+            return *this;
+        }
+
+        /**
+         * Adds a remove callback function, which will be called - on the Celix event thread -
+         * when a service match is being removed.
+         *
+         * The remove callback function has 3 arguments:
+         *  - A shared ptr to the removing service.
+         *  - A shared ptr to the removing service properties.
+         *  - A shared ptr to the bundle owning the removing service.
+         */
+        ServiceTrackerBuilder& addRemWithOwnerCallback(std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, const celix::Bundle&)> remove) {
+            remCallbacks.emplace_back([remove](std::shared_ptr<I> svc, std::shared_ptr<const celix::Properties> props, const celix::Bundle& bnd) {
+                remove(svc, props, bnd);
+            });
+            return *this;
+        }
+
+        /**
+         * Adds a set callback function, which will be called - on the Celix event thread -
+         * when there is a new highest ranking service match.
+         * This can can also be an empty match (nullptr).
+         *
+         * The set callback function has 2 arguments: A shared ptr to the highest
+         * ranking service match or nullptr.
+         */
+        ServiceTrackerBuilder& addSetCallback(std::function<void(std::shared_ptr<I>)> set) {
+            setCallbacks.emplace_back([set](std::shared_ptr<I> svc, std::shared_ptr<const celix::Properties>, std::shared_ptr<const celix::Bundle>) {
+                set(std::move(svc));
+            });
+            return *this;
+        }
+
+        /**
+         * Adds a set callback function, which will be called - on the Celix event thread -
+         * when there is a new highest ranking service match.
+         * This can can also be an empty match (nullptr).
+         *
+         * The set callback function has 2 arguments:
+         *  - A shared ptr to the highest ranking service match or nullptr.
+         *  - A const shared ptr to the set service properties or nullptr (if the service is nullptr).
+         */
+        ServiceTrackerBuilder& addSetWithPropertiesCallback(std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>)> set) {
+            setCallbacks.emplace_back([set](std::shared_ptr<I> svc, std::shared_ptr<const celix::Properties> props, std::shared_ptr<const celix::Bundle>) {
+                set(std::move(svc), std::move(props));
+            });
+            return *this;
+        }
+
+        /**
+         * Adds a set callback function, which will be called - on the Celix event thread -
+         * when there is a new highest ranking service match.
+         * This can can also be an empty match (nullptr).
+         *
+         * The set callback function has 3 arguments:
+         *  - A shared ptr to the highest ranking service match or nullptr.
+         *  - A const shared ptr to the set service properties or nullptr (if the service is nullptr).
+         *  - A const shared ptr to the bundle owning the set service or nullptr (if the service is nullptr).
+         */
+        ServiceTrackerBuilder& addSetWithOwner(std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, std::shared_ptr<const celix::Bundle>)> set) {
+            setCallbacks.emplace_back(std::move(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;
+        }
+
+        //TODO add function to register done call backs -> addOnStarted / addOnStopped (inheritance?)
+
+        /**
+         * "Builds" the service tracker and returns a ServiceTracker.
+         *
+         * The ServiceTracker will be started async.
+         */
+        std::shared_ptr<ServiceTracker<I>> build() {
+            auto tracker = std::make_shared<ServiceTracker<I>>(cCtx, std::move(name), std::move(versionRange), std::move(filter), std::move(setCallbacks), std::move(addCallbacks), std::move(remCallbacks));
+            tracker->open();
+            //TODO after moves , builder is not valid anymore. TBD howto handle this
+            return tracker;
+        }
+    private:
+        const std::shared_ptr<celix_bundle_context_t> cCtx;
+        std::string name;
+        std::string filter{};
+        std::string versionRange{};
+        std::vector<std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, std::shared_ptr<const celix::Bundle>)>> setCallbacks{};
+        std::vector<std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, const celix::Bundle&)>> addCallbacks{}; //TODO make bundle arg std::shared_ptr
+        std::vector<std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, const celix::Bundle&)>> remCallbacks{}; //TODO make bundle arg std::shared_ptr
+    };
+
+    /**
+     * Fluent builder API to track bundles.
+     * \warning Not thread safe.
+     */
+    class BundleTrackerBuilder {
+    private:
+        friend class BundleContext;
+
+        //NOTE private to prevent move so that a build() call cannot be forgotten
+        BundleTrackerBuilder(BundleTrackerBuilder &&) = default;
+    public:
+        explicit BundleTrackerBuilder(std::shared_ptr<celix_bundle_context_t> _cCtx) : cCtx{std::move(_cCtx)} {}
+
+        BundleTrackerBuilder &operator=(BundleTrackerBuilder &&) = delete;
+        BundleTrackerBuilder(const BundleTrackerBuilder &) = delete;
+        BundleTrackerBuilder operator=(const BundleTrackerBuilder &) = delete;
+
+        BundleTrackerBuilder& includeFrameworkBundleInCallback() {
+            includeFrameworkBundle = true;
+            return *this;
+        }
+
+        /**
+         * Adds a "on install" callback function, which will be called - on the Celix event thread -
+         * when a new bundle has been installed.
+         *
+         * The "on install" callback function has 1 arguments: A const reference to the installed bundle.
+         */
+        BundleTrackerBuilder& addOnInstallCallback(std::function<void(const celix::Bundle&)> callback) {
+            onInstallCallbacks.push_back(std::move(callback));
+            return *this;
+        }
+
+        /**
+         * Adds a "on start" callback function, which will be called - on the Celix event thread -
+         * when a new bundle has been started.
+         *
+         * The "on start" callback function has 1 arguments: A const reference to the started bundle.
+         */
+        BundleTrackerBuilder& addOnStartCallback(std::function<void(const celix::Bundle&)> callback) {
+            onStartCallbacks.push_back(std::move(callback));
+            return *this;
+        }
+
+        /**
+         * Adds a "on stop" callback function, which will be called - on the Celix event thread -
+         * when a new bundle has been stopped.
+         *
+         * The "on stop" callback function has 1 arguments: A const reference to the stopped bundle.
+         */
+        BundleTrackerBuilder& addOnStopCallback(std::function<void(const celix::Bundle&)> callback) {
+            onStopCallbacks.push_back(std::move(callback));
+            return *this;
+        }
+
+        /**
+         * "Builds" the bundle tracker and returns a BundleTracker.
+         *
+         * The BundleTracker will be started async.
+         */
+        std::shared_ptr<BundleTracker> build() {
+            auto tracker = std::make_shared<BundleTracker>(cCtx, includeFrameworkBundle, std::move(onInstallCallbacks), std::move(onStartCallbacks), std::move(onStopCallbacks));
+            tracker->open();
+            return tracker;
+        }
+    private:
+        const std::shared_ptr<celix_bundle_context_t> cCtx;
+        bool includeFrameworkBundle{false};
+        std::vector<std::function<void(const celix::Bundle&)>> onInstallCallbacks{};
+        std::vector<std::function<void(const celix::Bundle&)>> onStartCallbacks{};
+        std::vector<std::function<void(const celix::Bundle&)>> onStopCallbacks{};
+    };
+
+    /**
+     * Fluent builder API to track service trackers.
+     *
+     * \warning Not thread safe.
+     */
+    class MetaTrackerBuilder {
+    private:
+        friend class BundleContext;
+
+        //NOTE private to prevent move so that a build() call cannot be forgotten
+        MetaTrackerBuilder(MetaTrackerBuilder &&) = default;
+    public:
+        explicit MetaTrackerBuilder(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string _serviceName) :
+                cCtx{std::move(_cCtx)},
+                serviceName{std::move(_serviceName)}
+                {}
+
+        MetaTrackerBuilder &operator=(MetaTrackerBuilder &&) = delete;
+        MetaTrackerBuilder(const MetaTrackerBuilder &) = delete;
+        MetaTrackerBuilder operator=(const MetaTrackerBuilder &) = delete;
+
+        /**
+         * Adds a "on tracker created" callback function, which will be called - on the Celix event thread -
+         * when a new service tracker has been created.
+         *
+         * The "on tracker created" callback function has 1 arguments: A const reference to a ServiceTrackerInfo object.
+         */
+        MetaTrackerBuilder& addOnTrackerCreatedCallback(std::function<void(const ServiceTrackerInfo&)> cb) {
+            onTrackerCreated.emplace_back(std::move(cb));
+            return *this;
+        }
+
+        /**
+         * Adds a "on tracker destroyed" callback function, which will be called - on the Celix event thread -
+         * when a new service tracker has been destroyed.
+         *
+         * The "on tracker destroyed" callback function has 1 arguments: A const reference to a ServiceTrackerInfo object.
+         */
+        MetaTrackerBuilder& addOnTrackerDestroyedCallback(std::function<void(const ServiceTrackerInfo&)> cb) {
+            onTrackerDestroyed.emplace_back(std::move(cb));
+            return *this;
+        }
+
+        /**
+         * "Builds" the meta tracker and returns a MetaTracker.
+         *
+         * The MetaTracker will be started async.
+         */
+        std::shared_ptr<MetaTracker> build() {
+            auto tracker = std::make_shared<MetaTracker>(cCtx, std::move(serviceName), std::move(onTrackerCreated), std::move(onTrackerDestroyed));
+            tracker->open();
+            return tracker;
+        }
+    private:
+        const std::shared_ptr<celix_bundle_context_t> cCtx;
+        std::string serviceName;
+        std::vector<std::function<void(const ServiceTrackerInfo&)>> onTrackerCreated{};
+        std::vector<std::function<void(const ServiceTrackerInfo&)>> onTrackerDestroyed{};
+    };
+}
\ No newline at end of file
diff --git a/examples/celix-examples/hello_world_cxx/src/BundleActivator.cc b/libs/framework/include/celix/Bundle.h
similarity index 57%
copy from examples/celix-examples/hello_world_cxx/src/BundleActivator.cc
copy to libs/framework/include/celix/Bundle.h
index 2921ba7..b8aa71d 100644
--- a/examples/celix-examples/hello_world_cxx/src/BundleActivator.cc
+++ b/libs/framework/include/celix/Bundle.h
@@ -17,29 +17,33 @@
  * under the License.
  */
 
+#pragma once
+
 #include <memory>
-#include <iostream>
 
-#include <celix_api.h>
+#include "celix_bundle.h"
 
-namespace /*anon*/ {
+namespace celix {
 
-    class BundleActivator {
+    /**
+     * TODO
+     * \note Thread safe.
+     */
+    class Bundle {
     public:
-        BundleActivator(std::shared_ptr<celix::dm::DependencyManager> _mng) : mng{std::move(_mng)} {
-            std::cout << "Hello world from C++ bundle with id " << bndId() << std::endl;
-        }
-        ~BundleActivator() {
-            std::cout << "Goodbye world from C++ bundle with id " << bndId() << std::endl;
+        explicit Bundle(celix_bundle_t *_cBnd) : cBnd{_cBnd, [](celix_bundle_t*){/*nop*/}}{}
+
+        long getId() const { return celix_bundle_getId(cBnd.get()); }
+
+        std::string getEntry(const std::string& path) const {
+            std::string result{};
+            const char* entry = celix_bundle_getEntry(cBnd.get(), path.c_str());
+            if (entry != nullptr) {
+                result = std::string{entry};
+            }
+            return result;
         }
     private:
-        long bndId() const {
-            return celix_bundle_getId(celix_bundleContext_getBundle(mng->bundleContext()));
-        }
-
-        std::shared_ptr<celix::dm::DependencyManager> mng;
+        const std::shared_ptr<celix_bundle_t> cBnd;
     };
-
-}
-
-CELIX_GEN_CXX_BUNDLE_ACTIVATOR(BundleActivator)
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/libs/framework/include/celix/BundleActivator.h b/libs/framework/include/celix/BundleActivator.h
new file mode 100644
index 0000000..617a692
--- /dev/null
+++ b/libs/framework/include/celix/BundleActivator.h
@@ -0,0 +1,105 @@
+/*
+ * 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 <memory>
+#include <celix/dm/DependencyManager.h>
+
+#include "celix_bundle_activator.h"
+#include "celix/BundleContext.h"
+
+namespace celix {
+    namespace impl {
+
+        template<typename I>
+        struct BundleActivatorData {
+            std::weak_ptr<celix::BundleContext> ctx;
+            std::weak_ptr<celix::dm::DependencyManager> dm;
+            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 dm = std::make_shared<celix::dm::DependencyManager>(cCtx);
+            auto act = std::unique_ptr<I>(new I{ctx});
+            auto *data = new BundleActivatorData<I>{std::move(ctx), std::move(dm), 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 = std::make_shared<celix::dm::DependencyManager>(cCtx);
+            auto act = std::unique_ptr<I>(new I{dm});
+            dm->start();
+            auto *data = new BundleActivatorData<I>{std::move(ctx), std::move(dm), std::move(act)};
+            *out = (void *) data;
+            return CELIX_SUCCESS;
+        }
+
+        template<typename I>
+        celix_status_t destroyActivator(void *userData) {
+            auto *data = static_cast<BundleActivatorData<I> *>(userData);
+            data->bundleActivator = nullptr;
+            while (!data->ctx.expired()) {
+                std::cerr << "Cannot destroy bundle. Bundle context is still in use. Count is " << data->ctx.use_count()
+                          << std::endl;
+            }
+            delete data;
+            return CELIX_SUCCESS;
+        }
+    }
+}
+
+/**
+ * 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:
+ * - bundleActivator_create which allocates a pointer to the provided type.
+ * - bundleActivator_start/stop which will call the respectively provided typed start/stop functions.
+ * - bundleActivator_destroy will free the allocated for the provided type.
+ *
+ * @param type The activator type (e.g. 'ShellActivator'). A type which should have a constructor with a single arugment of std::shared_ptr<DependencyManager>.
+ */
+#define CELIX_GEN_CXX_BUNDLE_ACTIVATOR(actType)                                                                        \
+extern "C" celix_status_t bundleActivator_create(celix_bundle_context_t *context, void** userData) {                   \
+    return celix::impl::createActivator<actType>(context, userData);                                                         \
+}                                                                                                                      \
+                                                                                                                       \
+extern "C" celix_status_t bundleActivator_start(void *, celix_bundle_context_t *) {                                    \
+    /*nop*/                                                                                                            \
+    return CELIX_SUCCESS;                                                                                              \
+}                                                                                                                      \
+                                                                                                                       \
+extern "C" celix_status_t bundleActivator_stop(void *, celix_bundle_context_t*) {                                      \
+    /*nop*/                                                                                                            \
+    return CELIX_SUCCESS;                                                                                              \
+}                                                                                                                      \
+                                                                                                                       \
+extern "C" celix_status_t bundleActivator_destroy(void *userData, celix_bundle_context_t*) {                           \
+    return celix::impl::destroyActivator<actType>(userData);                                                                 \
+}                                                                                                                      \
+
diff --git a/libs/framework/include/celix/BundleContext.h b/libs/framework/include/celix/BundleContext.h
new file mode 100644
index 0000000..2136381
--- /dev/null
+++ b/libs/framework/include/celix/BundleContext.h
@@ -0,0 +1,505 @@
+/*
+ * 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 <memory>
+#include <mutex>
+#include <thread>
+#include <cstdarg>
+
+#include "celix_bundle_context.h"
+
+#include "celix/Builders.h"
+#include "celix/Properties.h"
+#include "celix/ServiceRegistration.h"
+#include "celix/Trackers.h"
+#include "celix/Bundle.h"
+#include "celix/Framework.h"
+
+#include "celix/dm/DependencyManager.h" //TODO, TBD include or forward declaration?
+
+namespace celix {
+
+    /**
+     * TODO
+     * \note Thread safe.
+     */
+    class BundleContext {
+    public:
+        explicit BundleContext(celix_bundle_context_t* _cCtx) :
+            cCtx{_cCtx, [](celix_bundle_context_t*){/*nop*/}},
+            dm{std::make_shared<celix::dm::DependencyManager>(_cCtx)},
+            bnd{celix_bundleContext_getBundle(_cCtx)} {}
+
+        /**
+         * Register a service in the Celix framework using a fluent builder API.
+         * The service registration can be fine tuned using the returned ServiceRegistrationBuilder API.
+         *
+         * Example:
+         *  shared_ptr<celix::BundleContext> ctx = ...
+         *  auto svcReg = ctx->registerService<IExample>(std::make_shared<ExampleImpl>())
+         *       .setVersion("1.0.0")
+         *       .addProperty("key1", "value1")
+         *       .addOnRegistered([](const std::shared_ptr<celix::ServiceRegistration>& reg) {
+         *           std::cout << "Done registering service '" << reg->getServiceName() << "' with id " << reg->getServiceId() << std::endl;
+         *       })
+         *       .build();
+         *
+         * @tparam I The service type (Note should be the abstract interface, not the interface implementer)
+         * @tparam Implementer The service implementer.
+         * @param implementer The service implementer.
+         * @param name The optional name of the service. If not provided celix::typeName<I> will be used to defer the service name.
+         * @return A ServiceRegistrationBuilder object.
+         */
+        template<typename I, typename Implementer>
+        ServiceRegistrationBuilder<I> registerService(std::shared_ptr<Implementer> implementer, const std::string& name = {}) {
+            std::shared_ptr<I> svc = implementer; //note Implement should be derived from I
+            return ServiceRegistrationBuilder<I>{cCtx, std::move(svc), celix::typeName<I>(name)};
+        }
+
+        /**
+         * Same as registerService, but then with an unmanaged service pointer.
+         * Note that the user is responsible for ensuring that the service pointer is valid as long
+         * as the service is registered in the Celix framework.
+         */
+        template<typename I>
+        ServiceRegistrationBuilder<I> registerUnmanagedService(I* svc, const std::string& name = {}) {
+            auto unmanagedSvc = std::shared_ptr<I>{svc, [](I*){/*nop*/}};
+            return ServiceRegistrationBuilder<I>{cCtx, std::move(unmanagedSvc), celix::typeName<I>(name)};
+        }
+
+        //TODO registerServiceFactory<I>()
+
+        /**
+         * Use a service registered in the Celix framework using a fluent builder API.
+         * The service use can be fine tuned using the returned UseServiceBuilder API.
+         *
+         * With this API a Celix service can be used by providing use functions.
+         * The use function will be executed on the Celix event thread and the Celix framework
+         * will ensure that the service cannot be removed while in use.
+         *
+         * If there are more 1 matching service, the highest ranking service will be used.
+         * If no service can be found the use callbacks with not be called.
+         *
+         *
+         * Example:
+         *  shared_ptr<celix::BundleContext> ctx = ...
+         *  auto callCount = ctx->useService<IExample>()
+         *       .setTimeout(std::chrono::seconds{1})
+         *       .addUseCallback([](IExample& service, const celix::Properties& props){
+         *           std::cout << "Calling service with id " << props.get("service.id", "-1") << std::endl;
+         *           service.method();
+         *       })
+         *       .build();
+         *
+         * @tparam I The service type to use
+         * @param name The optional service name to use. If not provided celix::typeName<I> will be used to defer the service name.
+         * @return A UseServiceBuilder object.
+         */
+        template<typename I>
+        UseServiceBuilder<I> useService(const std::string& name = {}) {
+            return UseServiceBuilder<I>{cCtx, celix::typeName<I>(name), true};
+        }
+
+        /**
+         * Use services registered in the Celix framework using a fluent builder API.
+         * The service use can be fine tuned using the returned UseServiceBuilder API.
+         *
+         * With this API Celix services can be used by providing use functions.
+         * The use function will be executed on the Celix event thread and the Celix framework
+         * will ensure that the service cannot be removed while in use.
+         *
+         * The use callbacks will be called for every matching service found.
+         *
+         * Example:
+         *  shared_ptr<celix::BundleContext> ctx = ...
+         *  auto callCount = ctx->useServices<IExample>()
+         *       .addUseCallback([](IExample& service, const celix::Properties& props){
+         *           std::cout << "Calling service with id " << props.get("service.id", "-1") << std::endl;
+         *           service.method();
+         *       })
+         *       .build();
+         *
+         * @tparam I The service type to use
+         * @param name The optional service name to use. If not provided celix::typeName<I> will be used to defer the service name.
+         * @return A UseServiceBuilder object.
+         */
+        template<typename I>
+        UseServiceBuilder<I> useServices(const std::string& name = {}) {
+            return UseServiceBuilder<I>{cCtx, celix::typeName<I>(name), false};
+        }
+
+        /**
+         * Finds the highest ranking service using the optional provided (LDAP) filter
+         * and version range.
+         * Note uses celix::typeName<I> to defer the service name.
+         *
+         * @tparam I the service type to found.
+         * @param filter An optional LDAP filter.
+         * @param versionRange An optional version range.
+         * @return The service id of the found service or -1 if the service was not found.
+         */
+        template<typename I>
+        long findService(const std::string& filter = {}, const std::string& versionRange = {}) {
+            return findServiceWithName<I>(celix::typeName<I>(), filter, versionRange);
+        }
+
+        /**
+         * Finds the highest ranking service using the provided service name and
+         * the optional (LDAP) filter and version range.
+         *
+         * @tparam I the service type to found.
+         * @param The service name. (Can be empty to find service with any name).
+         * @param filter An optional LDAP filter.
+         * @param versionRange An optional version range.
+         * @return The service id of the found service or -1 if the service was not found.
+         */
+        template<typename I>
+        long findServiceWithName(const std::string& name, const std::string& filter = {}, const std::string& versionRange = {}) {
+            celix_service_filter_options_t opts{};
+            opts.serviceName = name.empty() ? nullptr : name.c_str();
+            opts.filter = filter.empty() ? nullptr : filter.c_str();
+            opts.versionRange = versionRange.empty() ? nullptr : versionRange.c_str();
+            return celix_bundleContext_findServiceWithOptions(cCtx.get(), &opts);
+        }
+
+        /**
+         * Finds all services matching the optional provided (LDAP) filter
+         * and version range.
+         * Note uses celix::typeName<I> to defer the service name.
+         *
+         * @tparam I the service type to found.
+         * @param filter An optional LDAP filter.
+         * @param versionRange An optional version range.
+         * @return A vector of service ids.
+         */
+        template<typename I>
+        std::vector<long> findServices(const std::string& filter = {}, const std::string& versionRange = {}) {
+            return findServicesWithName<I>(celix::typeName<I>(), filter, versionRange);
+        }
+
+        /**
+         * Finds all service matching the provided service name and the optional (LDAP) filter
+         * and version range.
+         *
+         * @tparam I the service type to found.
+         * @param The service name. (Can be empty to find service with any name).
+         * @param filter An optional LDAP filter.
+         * @param versionRange An optional version range.
+         * @return A vector of service ids.
+         */
+        template<typename I>
+        std::vector<long> findServicesWithName(const std::string& name, const std::string& filter = {}, const std::string& versionRange = {}) {
+            celix_service_filter_options_t opts{};
+            opts.serviceName = name.empty() ? nullptr : name.c_str();
+            opts.filter = filter.empty() ? nullptr : filter.c_str();
+            opts.versionRange = versionRange.empty() ? nullptr : versionRange.c_str();
+
+            std::vector<long> result{};
+            auto cList = celix_bundleContext_findServicesWithOptions(cCtx.get(), &opts);
+            for (int i = 0; i < celix_arrayList_size(cList); ++i) {
+                long svcId = celix_arrayList_getLong(cList, i);
+                result.push_back(svcId);
+            }
+            celix_arrayList_destroy(cList);
+            return result;
+        }
+
+        /**
+         * Track services in the Celix framework using a fluent builder API.
+         * The service tracker can be fine tuned using the returned ServiceTrackerBuilder API.
+         *
+         * Example:
+         *  shared_ptr<celix::BundleContext> ctx = ...
+         *  auto tracker = ctx->trackServices<IExample>()
+         *       .setFilter("(property_key=value)")
+         *       .addAddCallback([](std::shared_ptr<IExample>, std::shared_ptr<const celix::Properties> props) {
+         *           std::cout << "Adding service with id '" << props->get("service.id", "-1") << std::endl;
+         *       })
+         *       .addRemCallback([](std::shared_ptr<IExample>, std::shared_ptr<const celix::Properties> props) {
+         *           std::cout << "Removing service with id '" << props->get("service.id", "-1") << std::endl;
+         *       })
+         *       .build();
+         *
+         * @tparam I The service type to track
+         * @param name The optional service name. If empty celix::typeName<I> will be used to defer the service name.
+         * @return A ServiceTrackerBuilder object.
+         */
+        template<typename I>
+        ServiceTrackerBuilder<I> trackServices(const std::string& name = {}) {
+            return ServiceTrackerBuilder<I>{cCtx, celix::typeName<I>(name)};
+        }
+
+        /**
+         * Same as trackerService, but than for any service.
+         * Note that the service shared ptr is of the type std::shared_ptr<void>.
+         */
+        ServiceTrackerBuilder<void> trackAnyServices() {
+            return ServiceTrackerBuilder<void>{cCtx, {}};
+        }
+
+        /**
+         * Track bundles in the Celix framework using a fluent builder API.
+         * The bundle tracker can be fine tuned using the returned BundleTrackerBuilder API.
+         *
+         * Example:
+         *  shared_ptr<celix::BundleContext> ctx = ...
+         *  auto tracker = ctx->trackBundles<>()
+         *       .addOnInstallCallback([](const celix::Bundle& bnd) {
+         *           std::cout << "Bundle installed with id '" << bnd.getId() << std::endl;
+         *       })
+         *       .build();
+         *
+         * @return A BundleTrackerBuilder object.
+         */
+        BundleTrackerBuilder trackBundles() {
+            return BundleTrackerBuilder{cCtx};
+        }
+
+        /**
+         * Track service trackers in the Celix framework using a fluent builder API.
+         * The meta tracker (service tracker tracker) can be fine tuned using the returned
+         * MetaTrackerBuilder API.
+         *
+         * Example:
+         *  shared_ptr<celix::BundleContext> ctx = ...
+         *  auto tracker = ctx->trackServiceTrackers<IExample>()
+         *       .addOnTrackerCreatedCallback([](const ServiceTrackerInfo& info) {
+         *           std::cout << "Tracker created for service name '" << info.serviceName << std::endl;
+         *       })
+         *       .addOnTrackerDestroyedCallback([](const ServiceTrackerInfo& info) {
+         *           std::cout << "Tracker destroyed for service name '" << info.serviceName << std::endl;
+         *       })
+         *       .build();
+         *
+         * @tparam I The service tracker service type to track.
+         * @param name The optional service name. If empty celix::typeName<I> will be used to defer the service name.
+         * @return A MetaTrackerBuilder object.
+         */
+        template<typename I>
+        MetaTrackerBuilder trackServiceTrackers(const std::string& name = {}) {
+            return MetaTrackerBuilder(cCtx, celix::typeName<I>(name));
+        }
+
+        /**
+         * Same as trackServiceTrackers, but than for service tracker for any service types.
+         */
+        MetaTrackerBuilder trackAnyServiceTrackers() {
+            return MetaTrackerBuilder(cCtx, {});
+        }
+
+        /**
+         * Install and optional start a bundle.
+         * Will silently ignore bundle ids < 0.
+         *
+         * @param bndLocation The bundle location to the bundle zip file.
+         * @param autoStart If the bundle should also be started.
+         * @return the bundleId (>= 0) or < 0 if the bundle could not be installed and possibly started.
+         */
+        long installBundle(const std::string& bndLocation, bool autoStart = true) {
+            return celix_bundleContext_installBundle(cCtx.get(), bndLocation.c_str(), autoStart);
+        }
+
+        /**
+         * Uninstall the bundle with the provided bundle id. If needed the bundle will be stopped first.
+         * Will silently ignore bundle ids < 0.
+         *
+         * @param bndId The bundle id to uninstall.
+         * @return true if the bundle is correctly uninstalled. False if not.
+         */
+        bool uninstallBundle(long bndId) {
+            return celix_bundleContext_uninstallBundle(cCtx.get(), bndId);
+        }
+
+        /**
+         * Start the bundle with the provided bundle id.
+         * Will silently ignore bundle ids < 0.
+         *
+         * @param bndId The bundle id to start.
+         * @return true if the bundle is found & correctly started. False if not.
+         */
+        bool startBundle(long bndId) {
+            return celix_bundleContext_startBundle(cCtx.get(), bndId);
+        }
+
+        /**
+         * Stop the bundle with the provided bundle id.
+         * Will silently ignore bundle ids < 0.
+         *
+         * @param bndId The bundle id to stop.
+         * @return true if the bundle is found & correctly stop. False if not.
+         */
+        bool stopBundle(long bndId) {
+            return celix_bundleContext_stopBundle(cCtx.get(), bndId);
+        }
+
+        /**
+         * Gets the config property - or environment variable if the config property does not exist - for the provided name.
+         * @param name The name of the property to receive.
+         * @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 {
+            return std::string{celix_bundleContext_getProperty(cCtx.get(), name.c_str(), defaultValue.c_str())};
+        }
+
+        /**
+         * Gets the config property - or environment variable if the config property does not exist - for the provided name.
+         * Only returns the value if it is a valid long.
+         *
+         * @param name The name of the property to receive.
+         * @param defaultVal The default value to use if the property is not found.
+         * @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 {
+            return celix_bundleContext_getPropertyAsLong(cCtx.get(), name.c_str(), defaultValue);
+        }
+
+        /**
+         * Gets the config property - or environment variable if the config property does not exist - for the provided name.
+         * Only returns the value if it is a valid double.
+         *
+         * @param name The name of the property to receive.
+         * @param defaultVal The default value to use if the property is not found.
+         * @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 {
+            return celix_bundleContext_getPropertyAsDouble(cCtx.get(), name.c_str(), defaultValue);
+        }
+
+        /**
+         * Gets the config property - or environment variable if the config property does not exist - for the provided name.
+         * Only returns the value if it is a valid boolean.
+         *
+         * @param name The name of the property to receive.
+         * @param defaultVal The default value to use if the property is not found.
+         * @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 {
+            return celix_bundleContext_getPropertyAsBool(cCtx.get(), name.c_str(), defaultValue);
+        }
+
+        /**
+         * Get the bundle of this bundle context.
+         */
+        const Bundle& getBundle() const {
+            return bnd;
+        }
+
+        /**
+         * Get the Celix framework for this bundle context.
+         */
+        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);
+        }
+
+        /**
+         * Get the Celix dependency manager for this bundle context
+         */
+        std::shared_ptr<dm::DependencyManager> getDependencyManager() const {
+            return dm;
+        }
+
+        /**
+         * Get the C bundle context.
+         */
+        celix_bundle_context_t* getCBundleContext() const {
+            return cCtx.get();
+        }
+
+        /**
+         * Logs a message to the Celix framework logger using the TRACE log level.
+         * NOTE only supports printf style call (so use c_str() instead of std::string)
+         */
+        void logTrace(const char* format...) {
+            va_list args;
+            va_start(args, format);
+            celix_bundleContext_vlog(cCtx.get(), CELIX_LOG_LEVEL_TRACE, format, args);
+            va_end(args);
+        }
+
+        /**
+         * Logs a message to the Celix framework logger using the DEBUG log level.
+         * NOTE only supports printf style call (so use c_str() instead of std::string)
+         */
+        void logDebug(const char* format...) {
+            va_list args;
+            va_start(args, format);
+            celix_bundleContext_vlog(cCtx.get(), CELIX_LOG_LEVEL_DEBUG, format, args);
+            va_end(args);
+        }
+
+        /**
+         * Logs a message to the Celix framework logger using the INFO log level.
+         * NOTE only supports printf style call (so use c_str() instead of std::string)
+         */
+        void logInfo(const char* format...) {
+            va_list args;
+            va_start(args, format);
+            celix_bundleContext_vlog(cCtx.get(), CELIX_LOG_LEVEL_INFO, format, args);
+            va_end(args);
+        }
+
+        /**
+         * Logs a message to the Celix framework logger using the WARNING log level.
+         * NOTE only supports printf style call (so use c_str() instead of std::string)
+         */
+        void logWarn(const char* format...) {
+            va_list args;
+            va_start(args, format);
+            celix_bundleContext_vlog(cCtx.get(), CELIX_LOG_LEVEL_WARNING, format, args);
+            va_end(args);
+        }
+
+        /**
+         * Logs a message to the Celix framework logger using the ERROR log level.
+         * NOTE only supports printf style call (so use c_str() instead of std::string)
+         */
+        void logError(const char* format...) {
+            va_list args;
+            va_start(args, format);
+            celix_bundleContext_vlog(cCtx.get(), CELIX_LOG_LEVEL_ERROR, format, args);
+            va_end(args);
+        }
+
+        /**
+         * Logs a message to the Celix framework logger using the FATAL log level.
+         * NOTE only supports printf style call (so use c_str() instead of std::string)
+         */
+        void logFatal(const char* format...) {
+            va_list args;
+            va_start(args, format);
+            celix_bundleContext_vlog(cCtx.get(), CELIX_LOG_LEVEL_FATAL, format, args);
+            va_end(args);
+        }
+
+    private:
+        const std::shared_ptr<celix_bundle_context_t> cCtx;
+        const std::shared_ptr<celix::dm::DependencyManager> dm;
+        const Bundle bnd;
+    };
+}
+
+
diff --git a/libs/framework/include/celix/Constants.h b/libs/framework/include/celix/Constants.h
new file mode 100644
index 0000000..d80a719
--- /dev/null
+++ b/libs/framework/include/celix/Constants.h
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+
+#include "celix_constants.h"
+
+namespace celix {
+
+    constexpr const char * const SERVICE_NAME = OSGI_FRAMEWORK_OBJECTCLASS;
+    constexpr const char * const SERVICE_ID = OSGI_FRAMEWORK_SERVICE_ID;
+    constexpr const char * const SERVICE_PID = OSGI_FRAMEWORK_SERVICE_PID;
+
+    constexpr const char * const SERVICE_RANKING = OSGI_FRAMEWORK_SERVICE_RANKING;
+    constexpr const char * const SERVICE_VERSION = CELIX_FRAMEWORK_SERVICE_VERSION;
+
+    constexpr const char * const FRAMEWORK_STORAGE = OSGI_FRAMEWORK_FRAMEWORK_STORAGE;
+    constexpr const char * const FRAMEWORK_STORAGE_USE_TMP_DIR = OSGI_FRAMEWORK_STORAGE_USE_TMP_DIR;
+    constexpr const char * const FRAMEWORK_UUID = OSGI_FRAMEWORK_FRAMEWORK_UUID;
+
+    constexpr const char * const BUNDLES_PATH_NAME = CELIX_BUNDLES_PATH_NAME;
+
+    constexpr const char * const LOAD_BUNDLES_WITH_NODELETE = CELIX_LOAD_BUNDLES_WITH_NODELETE;
+}
\ No newline at end of file
diff --git a/libs/framework/include/celix/Filter.h b/libs/framework/include/celix/Filter.h
new file mode 100644
index 0000000..de00f48
--- /dev/null
+++ b/libs/framework/include/celix/Filter.h
@@ -0,0 +1,91 @@
+/*
+ * 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 <memory>
+
+#include "celix/Properties.h"
+#include "celix_filter.h"
+
+namespace celix {
+
+    /**
+     * TODO
+     * \note Not thread safe.
+     */
+    class Filter {
+    public:
+        Filter() : cFilter{createFilter("")} {}
+        explicit Filter(const std::string& filterStr) : cFilter{createFilter(filterStr)} {}
+
+        Filter(Filter&&) = default;
+        Filter& operator=(Filter&&) = default;
+
+        Filter(const Filter& rhs) : cFilter{createFilter(rhs.getFilterString())} {}
+
+        Filter& operator=(const Filter& rhs) {
+            if (this != &rhs) {
+                cFilter = createFilter(rhs.getFilterString());
+            }
+            return *this;
+        }
+
+        //warning no ownership
+        static Filter wrap(celix_filter_t* f) {
+            return Filter{f};
+        }
+
+        std::string getFilterString() const {
+            auto cStr = celix_filter_getFilterString(cFilter.get());
+            return cStr == nullptr ? std::string{} : std::string{cStr};
+        }
+
+        bool match(const celix::Properties& props)  const {
+            return celix_filter_match(cFilter.get(), props.getCProperties());
+        }
+
+        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};
+        }
+
+        bool hasAttribute(const std::string& attributeKey) const {
+            return celix_filter_findAttribute(cFilter.get(), attributeKey.c_str()) != nullptr;
+        }
+
+        celix_filter_t* getCFilter() const {
+            return cFilter.get();
+        }
+    private:
+        static std::shared_ptr<celix_filter_t> createFilter(const std::string& filterStr) {
+            auto *cf = celix_filter_create(filterStr.c_str());
+            if (cf == nullptr) {
+                throw std::logic_error{"TODO create and throw celix (filter?) error. Invalid filter: '" + filterStr + "'"};
+            }
+            return std::shared_ptr<celix_filter_t>{cf, [](celix_filter_t *f) {
+                celix_filter_destroy(f);
+            }};
+        }
+
+        explicit Filter(celix_filter_t* f) : cFilter{f, [](celix_filter_t*){/*nop*/}} {}
+
+        std::shared_ptr<celix_filter_t> cFilter;
+    };
+}
\ No newline at end of file
diff --git a/examples/celix-examples/hello_world_cxx/src/BundleActivator.cc b/libs/framework/include/celix/Framework.h
similarity index 50%
copy from examples/celix-examples/hello_world_cxx/src/BundleActivator.cc
copy to libs/framework/include/celix/Framework.h
index 2921ba7..777565c 100644
--- a/examples/celix-examples/hello_world_cxx/src/BundleActivator.cc
+++ b/libs/framework/include/celix/Framework.h
@@ -17,29 +17,42 @@
  * under the License.
  */
 
+#pragma once
+
 #include <memory>
-#include <iostream>
 
-#include <celix_api.h>
+#include "celix_framework.h"
+
+namespace celix {
 
-namespace /*anon*/ {
+    class BundleContext; //forward declaration
 
-    class BundleActivator {
+    /**
+     * TODO
+     * \note Thread safe.
+     */
+    class Framework {
     public:
-        BundleActivator(std::shared_ptr<celix::dm::DependencyManager> _mng) : mng{std::move(_mng)} {
-            std::cout << "Hello world from C++ bundle with id " << bndId() << std::endl;
+        Framework(std::shared_ptr<celix::BundleContext> _fwCtx, celix_framework_t* _cFw) :
+            fwCtx{std::move(_fwCtx)},
+            cFw{std::shared_ptr<celix_framework_t >{_cFw, [](celix_framework_t*){/*nop*/}}}
+            {}
+
+        /**
+         * Get the framework UUID.
+         */
+        std::string getUUID() const {
+            return std::string{celix_framework_getUUID(cFw.get())};
         }
-        ~BundleActivator() {
-            std::cout << "Goodbye world from C++ bundle with id " << bndId() << std::endl;
+
+        /**
+         * Get the bundle context for the framework.
+         */
+        std::shared_ptr<celix::BundleContext> getFrameworkBundleContext() const {
+            return fwCtx;
         }
     private:
-        long bndId() const {
-            return celix_bundle_getId(celix_bundleContext_getBundle(mng->bundleContext()));
-        }
-
-        std::shared_ptr<celix::dm::DependencyManager> mng;
+        const std::shared_ptr<celix::BundleContext> fwCtx;
+        const std::shared_ptr<celix_framework_t> cFw;
     };
-
-}
-
-CELIX_GEN_CXX_BUNDLE_ACTIVATOR(BundleActivator)
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/libs/framework/include/celix/Properties.h b/libs/framework/include/celix/Properties.h
new file mode 100644
index 0000000..c0c587b
--- /dev/null
+++ b/libs/framework/include/celix/Properties.h
@@ -0,0 +1,217 @@
+/*
+ * 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 <map>
+#include <unordered_map>
+#include <memory>
+
+#include "celix_properties.h"
+#include "utils.h"
+#include "hash_map.h"
+
+namespace celix {
+
+    class PropertiesIterator {
+    public:
+        explicit PropertiesIterator(celix_properties_t* props) : iter{hashMapIterator_construct((hash_map_t*)props)} { next(); }
+        explicit PropertiesIterator(const celix_properties_t* props) : iter{hashMapIterator_construct((hash_map_t*)props)} { next(); }
+
+        PropertiesIterator& operator++() {
+            next();
+            return *this;
+        }
+
+        PropertiesIterator& operator*() {
+            return *this;
+        }
+
+        bool operator==(const celix::PropertiesIterator& rhs) const {
+            bool sameMap = iter.map == rhs.iter.map;
+            bool sameIndex = iter.index == rhs.iter.index;
+            bool oneIsEnd = end || rhs.end;
+            if (oneIsEnd) {
+                return sameMap && end && rhs.end;
+            } else {
+                return sameMap && sameIndex;
+            }
+        }
+
+        bool operator!=(const celix::PropertiesIterator& rhs) const {
+            return !operator==(rhs);
+        }
+
+        void next() {
+            if (hashMapIterator_hasNext(&iter)) {
+                auto* entry = hashMapIterator_nextEntry(&iter);
+                auto *k = hashMapEntry_getKey(entry);
+                auto *v = hashMapEntry_getValue(entry);
+                first = std::string{(const char*)k};
+                second = std::string{(const char*)v};
+            } else {
+                moveToEnd();
+            }
+        }
+
+        void moveToEnd() {
+            end = true;
+            first = {};
+            second = {};
+        }
+
+        std::string first{};
+        std::string second{};
+    private:
+        hash_map_iterator_t iter;
+        bool end{false};
+    };
+
+    /**
+     * TODO
+     * \warning Not thread safe.
+     */
+    class Properties {
+    public:
+        using const_iterator = PropertiesIterator;
+
+        class ValueRef {
+        public:
+            ValueRef(std::shared_ptr<celix_properties_t> _props, std::string _key) : props{std::move(_props)}, key{std::move(_key)} {}
+            ValueRef& operator=(const std::string& value) {
+                celix_properties_set(props.get(), key.c_str(), value.c_str());
+                return *this;
+            }
+
+            const char* getValue() const {
+                return celix_properties_get(props.get(), key.c_str(), nullptr);
+            }
+
+            operator std::string() const {
+                auto *cstr = getValue();
+                return cstr == nullptr ? std::string{} : std::string{cstr};
+            }
+        private:
+            std::shared_ptr<celix_properties_t> props;
+            std::string key;
+        };
+
+
+        Properties() : cProps{celix_properties_create(), [](celix_properties_t* p) { celix_properties_destroy(p); }} {}
+
+        Properties(Properties&&) = default;
+        Properties& operator=(Properties&&) = default;
+
+        Properties& operator=(const Properties &rhs) {
+            if (this != &rhs) {
+                cProps = std::shared_ptr<celix_properties_t>{celix_properties_copy(rhs.cProps.get()), [](celix_properties_t* p) { celix_properties_destroy(p); }};
+            }
+            return *this;
+        }
+
+        Properties(const Properties& rhs) :
+            cProps{celix_properties_copy(rhs.cProps.get()), [](celix_properties_t* p) { celix_properties_destroy(p); }} {}
+
+        /**
+         * Wraps C properties, but does not take ownership -> dtor will not destroy properties
+         */
+        static std::shared_ptr<const Properties> wrap(const celix_properties_t* wrapProps) {
+            auto* cp = const_cast<celix_properties_t*>(wrapProps);
+            return std::shared_ptr<const Properties>{new Properties{cp}};
+        }
+
+        celix_properties_t* getCProperties() const {
+            return cProps.get();
+        }
+
+        ValueRef operator[](std::string key) {
+            return ValueRef{cProps, std::move(key)};
+        }
+
+        const_iterator begin() const noexcept {
+            return PropertiesIterator{cProps.get()};
+        }
+
+        const_iterator end() const noexcept {
+            auto iter = PropertiesIterator{cProps.get()};
+            iter.moveToEnd();
+            return iter;
+        }
+
+        const_iterator cbegin() const noexcept {
+            return PropertiesIterator{cProps.get()};
+        }
+
+        const_iterator cend() const noexcept {
+            auto iter = PropertiesIterator{cProps.get()};
+            iter.moveToEnd();
+            return iter;
+        }
+
+        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};
+        }
+
+        long getAsLong(const std::string& key, long defaultValue) const {
+            return celix_properties_getAsLong(cProps.get(), key.c_str(), defaultValue);
+        }
+
+        double getAsDouble(const std::string &key, double defaultValue) const {
+            return celix_properties_getAsDouble(cProps.get(), key.c_str(), defaultValue);
+        }
+
+        bool getAsBool(const std::string &key, bool defaultValue) const {
+            return celix_properties_getAsBool(cProps.get(), key.c_str(), defaultValue);
+        }
+
+        void set(const std::string& key, const std::string& value) {
+            celix_properties_set(cProps.get(), key.c_str(), value.c_str());
+        }
+
+        void set(const std::string& key, const char* value) {
+            celix_properties_set(cProps.get(), key.c_str(), value);
+        }
+
+        void set(const std::string& key, bool value) {
+            celix_properties_setBool(cProps.get(), key.c_str(), value);
+        }
+
+        template<typename T>
+        void set(const std::string& key, T value) {
+            using namespace std;
+            celix_properties_set(cProps.get(), key.c_str(), to_string(value).c_str());
+        }
+
+        std::size_t size() const {
+            return celix_properties_size(cProps.get());
+        }
+    private:
+        explicit Properties(celix_properties_t* props) : cProps{props, [](celix_properties_t*) { /*nop*/ }} {}
+
+        std::shared_ptr<celix_properties_t> cProps;
+    };
+
+}
+
+inline std::ostream& operator<<(std::ostream& os, const ::celix::Properties::ValueRef& ref)
+{
+    os << std::string{ref.getValue()}; //TODO improve
+    return os;
+}
\ No newline at end of file
diff --git a/libs/framework/include/celix/dm/Properties.h b/libs/framework/include/celix/ServiceFactory.h
similarity index 67%
copy from libs/framework/include/celix/dm/Properties.h
copy to libs/framework/include/celix/ServiceFactory.h
index 5c33885..8f561c4 100644
--- a/libs/framework/include/celix/dm/Properties.h
+++ b/libs/framework/include/celix/ServiceFactory.h
@@ -19,9 +19,18 @@
 
 #pragma once
 
-#include <map>
-#include <string>
+#include "celix/Bundle.h"
+#include "celix/Properties.h"
 
-namespace celix { namespace dm {
-    using Properties = std::map<std::string, std::string>;
-}}
+namespace celix {
+
+    template<typename I>
+    class ServiceFactory {
+    public:
+        virtual ~ServiceFactory() = default;
+
+        virtual std::shared_ptr<I> createBundleSpecificService(const celix::Bundle& requestingBundle, const celix::Properties svcFactoryProperties) = 0;
+        //TODO,TBD need ungetService variant?
+    };
+
+}
\ No newline at end of file
diff --git a/libs/framework/include/celix/ServiceRegistration.h b/libs/framework/include/celix/ServiceRegistration.h
new file mode 100644
index 0000000..1a2cd02
--- /dev/null
+++ b/libs/framework/include/celix/ServiceRegistration.h
@@ -0,0 +1,176 @@
+/*
+ * 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 <memory>
+#include <mutex>
+#include <vector>
+#include <functional>
+
+#include "celix/Properties.h"
+#include "celix_bundle_context.h"
+
+namespace celix {
+
+    enum class ServiceRegistrationState {
+        REGISTERING,
+        REGISTERED,
+        UNREGISTERING,
+        UNREGISTERED
+    };
+
+    class ServiceRegistration;
+
+    /**
+     * TODO
+     * \note Thread safe.
+     */
+    class ServiceRegistration  {
+    public:
+        ServiceRegistration(
+                std::shared_ptr<celix_bundle_context_t> _cCtx,
+                std::shared_ptr<void> _svc,
+                std::string _name,
+                std::string _version,
+                celix::Properties _properties,
+                std::vector<std::function<void(const std::shared_ptr<ServiceRegistration>&)>> _onRegisteredCallbacks,
+                std::vector<std::function<void(const std::shared_ptr<ServiceRegistration>&)>> _onUnregisteredCallbacks) :
+                cCtx{std::move(_cCtx)},
+                svc{std::move(_svc)},
+                name{std::move(_name)},
+                version{std::move(_version)},
+                properties{std::move(_properties)},
+                onRegisteredCallbacks{std::move(_onRegisteredCallbacks)},
+                onUnregisteredCallbacks{std::move(_onUnregisteredCallbacks)} {
+
+            //setup registration using C api.
+            auto* cProps = celix_properties_copy(properties.getCProperties());
+            celix_service_registration_options_t opts{};
+            opts.svc = svc.get();
+            opts.serviceName = name.c_str();
+            opts.properties = cProps;
+            if (!version.empty()) {
+                opts.serviceVersion = version.c_str();
+            }
+            opts.asyncData = static_cast<void*>(this);
+            opts.asyncCallback = [](void *data, long /*svcId*/) {
+                auto *reg = static_cast<ServiceRegistration *>(data);
+                {
+                    std::lock_guard<std::mutex> lck{reg->mutex};
+                    reg->state = ServiceRegistrationState::REGISTERED;
+                }
+                auto regAsSharedPtr = reg->getSelf();
+                for (const auto &cb : reg->onRegisteredCallbacks) {
+                    cb(regAsSharedPtr);
+                }
+            };
+            std::lock_guard<std::mutex> lck{mutex};
+            svcId = celix_bundleContext_registerServiceWithOptionsAsync(cCtx.get(), &opts);
+        }
+
+        ~ServiceRegistration() noexcept {
+            wait(); //if state is REGISTERING, wait till registration is complete.
+            unregister(); //if state is REGISTERED, queue unregistration.
+            wait(); //if state is UNREGISTERING, wait till unregistration is complete.
+        }
+
+        const std::string& getServiceName() const { return name; }
+        const std::string& getServiceVersion() const { return version; }
+        const celix::Properties& getServiceProperties() const { return properties; }
+
+        ServiceRegistrationState getCurrentState() const {
+            std::lock_guard<std::mutex> lck{mutex};
+            return state;
+        }
+
+        long getServiceId() const {
+            std::lock_guard<std::mutex> lck{mutex};
+            return svcId;
+        }
+
+        void wait() const {
+            bool needWaitUnregistering = false;
+            bool needWaitRegistering = false;
+            long localId;
+            {
+                std::lock_guard<std::mutex> lck{mutex};
+                localId = svcId;
+                if (state == ServiceRegistrationState::REGISTERING) {
+                    needWaitRegistering = true;
+                } else if (state == ServiceRegistrationState::UNREGISTERING) {
+                    needWaitUnregistering = true;
+                }
+            }
+            if (needWaitRegistering) {
+                celix_bundleContext_waitForAsyncRegistration(cCtx.get(), localId);
+            }
+            if (needWaitUnregistering) {
+                celix_bundleContext_waitForAsyncUnregistration(cCtx.get(), localId);
+            }
+        }
+
+        void unregister() {
+            wait(); //if state is REGISTERING, wait till registration is complete.
+            std::lock_guard<std::mutex> lck{mutex};
+            if (state == ServiceRegistrationState::REGISTERED) {
+                //not yet unregistering
+                state = ServiceRegistrationState::UNREGISTERING;
+
+                //NOTE: As long this unregister event is in the queue, the ServiceRegistration will wait in its dtor
+                celix_bundleContext_unregisterServiceAsync(cCtx.get(), svcId, this, [](void *data) {
+                    auto reg = static_cast<ServiceRegistration*>(data);
+                    {
+                        std::lock_guard<std::mutex> lck{reg->mutex};
+                        reg->state = ServiceRegistrationState::UNREGISTERED;
+                    }
+                    auto regAsSharedPtr = reg->getSelf();
+                    for (const auto &cb : reg->onUnregisteredCallbacks) {
+                        cb(regAsSharedPtr);
+                    }
+                });
+            }
+        }
+
+        void setSelf(const std::shared_ptr<ServiceRegistration>& s) {
+            std::lock_guard<std::mutex> lck{mutex};
+            self = s;
+        }
+
+        std::shared_ptr<ServiceRegistration> getSelf() const {
+            std::lock_guard<std::mutex> lck{mutex};
+            return self.lock();
+        }
+
+    private:
+        const std::shared_ptr<celix_bundle_context_t> cCtx;
+        const std::shared_ptr<void> svc;
+        const std::string name;
+        const std::string version;
+        const celix::Properties properties;
+        const std::vector<std::function<void(const std::shared_ptr<ServiceRegistration>&)>> onRegisteredCallbacks;
+        const std::vector<std::function<void(const std::shared_ptr<ServiceRegistration>&)>> onUnregisteredCallbacks;
+
+        mutable std::mutex mutex{}; //protects below
+        long svcId{-1};
+        ServiceRegistrationState state{ServiceRegistrationState::REGISTERING};
+        std::weak_ptr<ServiceRegistration> self{}; //weak ptr to self, so that callbacks can receive a shared ptr
+    };
+
+}
\ No newline at end of file
diff --git a/libs/framework/include/celix/Trackers.h b/libs/framework/include/celix/Trackers.h
new file mode 100644
index 0000000..e2a6b70
--- /dev/null
+++ b/libs/framework/include/celix/Trackers.h
@@ -0,0 +1,481 @@
+/*
+ * 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 <memory>
+#include <mutex>
+#include <atomic>
+#include <cassert>
+
+#include "celix/Properties.h"
+#include "celix/Utils.h"
+#include "celix/Bundle.h"
+#include "celix/Constants.h"
+#include "celix/Filter.h"
+#include "celix_bundle_context.h"
+
+namespace celix {
+
+    /**
+     * TODO
+     * GenericTracker <-----------------------------------
+     *  ^                               |                |
+     *  |                               |                |
+     *  GenericServiceTracker      BundleTracker    MetaTracker
+     *  ^
+     *  |
+     *  ServiceTracker<I>
+     */
+
+    enum class TrackerState {
+        OPENING,
+        OPEN,
+        CLOSING,
+        CLOSED
+    };
+
+    /**
+     * TODO
+     * \note Thread safe.
+     */
+    class AbstractTracker {
+    public:
+        explicit AbstractTracker(std::shared_ptr<celix_bundle_context_t> _cCtx) : cCtx{std::move(_cCtx)} {}
+
+        virtual ~AbstractTracker() noexcept {
+            //NOTE that the tracker needs to be closed in the specialization (when all fields are still valid)
+            assert(getCurrentState() == TrackerState::CLOSED);
+        }
+
+        bool isOpen() const {
+            std::lock_guard<std::mutex> lck{mutex};
+            return state == TrackerState::OPEN;
+        }
+
+        TrackerState getCurrentState() const {
+            std::lock_guard<std::mutex> lck{mutex};
+            return state;
+        }
+
+        void close() {
+            wait(); //if state is OPENING, wait till tracker is opened
+            std::lock_guard<std::mutex> lck{mutex};
+            if (state == TrackerState::OPEN) {
+                //not yet closed
+                state = TrackerState::CLOSING;
+
+                celix_bundleContext_stopTrackerAsync(cCtx.get(), trkId, this, [](void *data) {
+                    auto trk = static_cast<AbstractTracker*>(data);
+                    std::lock_guard<std::mutex> lck{trk->mutex};
+                    trk->state = TrackerState::CLOSED;
+                    trk->trkId = -1L;
+                });
+            }
+        }
+
+        //note needs override, open is different per tracker type
+        virtual void open() {}
+
+        void wait() const {
+            bool needWaitOpening = false;
+            bool needWaitClosing = false;
+            long localId;
+            {
+                std::lock_guard<std::mutex> lck{mutex};
+                localId = trkId;
+                if (state == TrackerState::OPENING) {
+                    needWaitOpening = true;
+                } else if (state == TrackerState::CLOSING) {
+                    needWaitClosing = true;
+                }
+            }
+            if (needWaitOpening) {
+                celix_bundleContext_waitForAsyncTracker(cCtx.get(), localId);
+            }
+            if (needWaitClosing) {
+                celix_bundleContext_waitForAsyncStopTracker(cCtx.get(), localId);
+            }
+        }
+    protected:
+        const std::shared_ptr<celix_bundle_context_t> cCtx;
+        mutable std::mutex mutex{}; //protects below
+        long trkId{-1L};
+        TrackerState state{TrackerState::CLOSED};
+    };
+
+    /**
+     * TODO
+     * \note Thread safe.
+     */
+    class GenericServiceTracker : public AbstractTracker {
+    public:
+        GenericServiceTracker(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string _svcName,
+                              std::string _svcVersionRange, std::string _filter) : AbstractTracker{std::move(_cCtx)}, svcName{std::move(_svcName)},
+                                                                                   svcVersionRange{std::move(_svcVersionRange)}, filter{std::move(_filter)} {
+            opts.trackerCreatedCallbackData = this;
+            opts.trackerCreatedCallback = [](void *data) {
+                auto* trk = static_cast<GenericServiceTracker*>(data);
+                std::lock_guard<std::mutex> callbackLock{trk->mutex};
+                trk->state = TrackerState::OPEN;
+            };
+        }
+
+        ~GenericServiceTracker() override = default;
+
+        void open() override {
+            wait(); //if state is CLOSING, wait till tracker is closed
+            std::lock_guard<std::mutex> lck{mutex};
+            if (state == TrackerState::CLOSED) {
+                state = TrackerState::OPENING;
+
+                //NOTE assuming the opts already configured the callbacks
+                trkId = celix_bundleContext_trackServicesWithOptionsAsync(cCtx.get(), &opts);
+            }
+        }
+
+        const std::string& getServiceName() const { return svcName; }
+
+        const std::string& getServiceRange() const { return svcVersionRange; }
+
+        const std::string& getFilter() const { return filter; }
+
+        std::size_t getServiceCount() const {
+            return svcCount;
+        }
+    protected:
+        const std::string svcName;
+        const std::string svcVersionRange;
+        const std::string filter;
+        celix_service_tracking_options opts{}; //note only set in the ctor
+        std::atomic<size_t> svcCount{0};
+    };
+
+    /**
+     * \note Thread safe.
+     * @tparam I The service type to track
+     */
+    template<typename I>
+    class ServiceTracker : public GenericServiceTracker {
+    public:
+        ServiceTracker(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string _svcName,
+                       std::string _svcVersionRange, std::string _filter,
+                       std::vector<std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, std::shared_ptr<const celix::Bundle>)>> _setCallbacks,
+                       std::vector<std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, const celix::Bundle&)>> _addCallbacks,
+                       std::vector<std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, const celix::Bundle&)>> _remCallbacks) :
+                       GenericServiceTracker{std::move(_cCtx), std::move(_svcName), std::move(_svcVersionRange), std::move(_filter)},
+                       setCallbacks{std::move(_setCallbacks)},
+                       addCallbacks{std::move(_addCallbacks)},
+                       remCallbacks{std::move(_remCallbacks)} {
+
+            opts.filter.serviceName = svcName.empty() ? nullptr : svcName.c_str();
+            opts.filter.versionRange = svcVersionRange.empty() ? nullptr : svcVersionRange.c_str();
+            opts.filter.filter = filter.empty() ? nullptr : filter.c_str();
+            opts.callbackHandle = this;
+            opts.addWithOwner = [](void *handle, void *voidSvc, const celix_properties_t* cProps, const celix_bundle_t* cBnd) {
+                auto tracker = static_cast<ServiceTracker<I>*>(handle);
+                long svcId = celix_properties_getAsLong(cProps, OSGI_FRAMEWORK_SERVICE_ID, -1L);
+                auto svc = tracker->getServiceForId(svcId, voidSvc);
+                auto props = tracker->getPropertiesForId(svcId, cProps);
+                //TODO auto bnd = tracker->getBundleForId(svcId, cBnd);
+                auto bnd = celix::Bundle{const_cast<celix_bundle_t*>(cBnd)};
+                for (const auto& func : tracker->addCallbacks) {
+                    func(svc, props, bnd);
+                }
+                tracker->svcCount.fetch_add(1, std::memory_order_relaxed);
+            };
+            opts.removeWithOwner = [](void *handle, void *voidSvc, const celix_properties_t* cProps, const celix_bundle_t* cBnd) {
+                auto tracker = static_cast<ServiceTracker<I>*>(handle);
+                long svcId = celix_properties_getAsLong(cProps, OSGI_FRAMEWORK_SERVICE_ID, -1L);
+                auto svc = tracker->getServiceForId(svcId, voidSvc);
+                auto props = tracker->getPropertiesForId(svcId, cProps);
+                //TODO auto bnd = tracker->getBundleForId(svcId, cBnd);
+                auto bnd = celix::Bundle{const_cast<celix_bundle_t*>(cBnd)};
+                for (const auto& func : tracker->remCallbacks) {
+                    func(svc, props, bnd);
+                }
+                tracker->svcCount.fetch_sub(1, std::memory_order_relaxed);
+
+                tracker->removeAddRemEntriesForId(svcId);
+                tracker->waitForExpire(std::move(svc), svcId, "Service");
+                tracker->waitForExpire(std::move(props), svcId, "celix::Properties");
+            };
+            opts.setWithOwner = [](void *handle, void *voidSvc, const celix_properties_t* cProps, const celix_bundle_t* cBnd) {
+                auto tracker = static_cast<ServiceTracker<I>*>(handle);
+                std::shared_ptr<I> svc{};
+                std::shared_ptr<const celix::Properties> props{};
+                std::shared_ptr<const celix::Bundle> bnd{};
+                if (voidSvc != nullptr) {
+                    svc = std::shared_ptr<I>{static_cast<I*>(voidSvc), [](I*){/*nop*/}};
+                }
+                if (cProps != nullptr) {
+                    props = celix::Properties::wrap(cProps);
+                }
+                if (cBnd != nullptr) {
+                    bnd = std::make_shared<celix::Bundle>(const_cast<celix_bundle_t*>(cBnd));
+                }
+
+                long prevSvcId = -1L;
+                std::shared_ptr<I> prevSvc{};
+                std::shared_ptr<const celix::Properties> prevProps{};
+                std::shared_ptr<const celix::Bundle> prevBnd{};
+                {
+                    std::lock_guard<std::mutex> lck{tracker->mutex};
+                    if (tracker->currentSetServiceId >= 0) {
+                        prevSvcId = tracker->currentSetServiceId;
+                        prevSvc = tracker->currentSetService;
+                        prevProps = tracker->currentSetProperties;
+                        prevBnd = tracker->currentSetBundle;
+                    }
+                    tracker->currentSetService = svc;
+                    tracker->currentSetProperties = props;
+                    tracker->currentSetBundle = bnd;
+                    tracker->currentSetServiceId = props ? props->getAsLong(celix::SERVICE_ID, -1) : -1L;
+                }
+                for (const auto& func : tracker->setCallbacks) {
+                    func(svc, props, bnd);
+                }
+                if (prevSvcId >= 0) {
+                    tracker->waitForExpire(std::move(prevSvc), prevSvcId, "service");
+                    tracker->waitForExpire(std::move(prevProps), prevSvcId, "celix::Properties");
+                    tracker->waitForExpire(std::move(prevBnd), prevSvcId, "celix::Bundle");
+                }
+            };
+        }
+
+        ~ServiceTracker() override {
+            close(); //if state is OPEN, queue stop tracker.
+            wait(); //if state is CLOSING, wait till closing is complete.
+        }
+    private:
+        std::shared_ptr<I> getServiceForId(long id, void* voidSvc) {
+            std::lock_guard<std::mutex> lck{mutex};
+            auto it = services.find(id);
+            if (it == services.end()) {
+                //new
+                auto svc = std::shared_ptr<I>{static_cast<I*>(voidSvc), [](I*){/*nop*/}};
+                services[id] = svc;
+                return svc;
+            } else {
+                return it->second;
+            }
+        }
+
+        std::shared_ptr<const celix::Properties> getPropertiesForId(long id, const celix_properties_t * cProps) {
+            std::lock_guard<std::mutex> lck{mutex};
+            auto it = properties.find(id);
+            if (it == properties.end()) {
+                //new
+                auto props = celix::Properties::wrap(cProps);
+                properties[id] = props;
+                return props;
+            } else {
+                return it->second;
+            }
+        }
+
+        void waitForExpire(std::shared_ptr<const void> ptr, long observeSvcId, const char *objName) {
+            //observe.expired() not working
+            std::weak_ptr<const void> observe = ptr;
+            ptr = nullptr;
+            while (!observe.expired()) {
+                celix_bundleContext_log(cCtx.get(), CELIX_LOG_LEVEL_WARNING, "Cannot remove %s associated with service.id %li, because it is in use. Current shared_ptr use count is %i\n", objName, observeSvcId, (int)observe.use_count());
+                std::this_thread::sleep_for(std::chrono::seconds (1));
+            }
+        }
+
+        void removeAddRemEntriesForId(long svcId) {
+            std::lock_guard<std::mutex> lck{mutex};
+            services.erase(svcId);
+            properties.erase(svcId);
+            //bundles.erase(svcId);
+        }
+
+        const std::vector<std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, std::shared_ptr<const celix::Bundle>)>> setCallbacks;
+        const std::vector<std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, const celix::Bundle&)>> addCallbacks;
+        const std::vector<std::function<void(std::shared_ptr<I>, std::shared_ptr<const celix::Properties>, const celix::Bundle&)>> remCallbacks;
+
+        mutable std::mutex mutex{}; //protect below
+
+        std::unordered_map<long, std::shared_ptr<I>> services{};
+        std::unordered_map<long, std::shared_ptr<const celix::Properties>> properties{};
+        //std::unordered_map<long, std::shared_ptr<const celix::Bundle>> bundles{}; TODO
+
+        std::shared_ptr<I> currentSetService{};
+        std::shared_ptr<const celix::Properties> currentSetProperties{};
+        std::shared_ptr<const celix::Bundle> currentSetBundle{};
+        long currentSetServiceId{-1L};
+
+//        std::unordered_map<SvcOrder, SvcEntry<I>{}; //TODO for update
+    };
+
+    /**
+     * \note Thread safe.
+     */
+    class BundleTracker : public AbstractTracker {
+    public:
+        BundleTracker(
+                std::shared_ptr<celix_bundle_context_t> _cCtx,
+                bool _includeFrameworkBundle,
+                std::vector<std::function<void(const celix::Bundle&)>> _onInstallCallbacks,
+                std::vector<std::function<void(const celix::Bundle&)>> _onStartCallbacks,
+                std::vector<std::function<void(const celix::Bundle&)>> _onStopCallbacks) :
+                AbstractTracker{std::move(_cCtx)},
+                includeFrameworkBundle{_includeFrameworkBundle},
+                onInstallCallbacks{std::move(_onInstallCallbacks)},
+                onStartCallbacks{std::move(_onStartCallbacks)},
+                onStopCallbacks{std::move(_onStopCallbacks)} {
+
+            opts.includeFrameworkBundle = includeFrameworkBundle;
+            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)};
+                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)};
+                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)};
+                for (const auto& cb : tracker->onStopCallbacks) {
+                    cb(bnd);
+                }
+            };
+            opts.trackerCreatedCallbackData = this;
+            opts.trackerCreatedCallback = [](void *data) {
+                auto* trk = static_cast<BundleTracker*>(data);
+                std::lock_guard<std::mutex> callbackLock{trk->mutex};
+                trk->state = TrackerState::OPEN;
+            };
+        }
+
+        ~BundleTracker() override {
+            close(); //if state is OPEN, queue stop tracker.
+            wait(); //if state is CLOSING, wait till closing is complete.
+        }
+
+        void open() override {
+            wait(); //if state is CLOSING, wait till tracker is closed
+            std::lock_guard<std::mutex> lck{mutex};
+            if (state == TrackerState::CLOSED) {
+                state = TrackerState::OPENING;
+
+                //NOTE the opts already configured the callbacks
+                trkId = celix_bundleContext_trackBundlesWithOptionsAsync(cCtx.get(), &opts);
+            }
+        }
+    private:
+        const bool includeFrameworkBundle;
+        const std::vector<std::function<void(const celix::Bundle&)>> onInstallCallbacks;
+        const std::vector<std::function<void(const celix::Bundle&)>> onStartCallbacks;
+        const std::vector<std::function<void(const celix::Bundle&)>> onStopCallbacks;
+        celix_bundle_tracking_options_t opts{}; //note only set in the ctor
+    };
+
+
+    /**
+     * A trivial struct containing information about a service tracker.
+     */
+    struct ServiceTrackerInfo {
+        /**
+         * The service name the service tracker is tracking.
+         * Will be '*' if the service tracker is tracking any services.
+         */
+        const std::string serviceName;
+
+        /**
+         * The service filter the service tracker is using for tracking.
+         */
+        const celix::Filter filter;
+
+        /**
+         * The bundle id of the owner of the service tracker.
+         */
+        const long trackerOwnerBundleId;
+    };
+
+    /**
+     * \note Thread safe.
+     */
+    class MetaTracker : public AbstractTracker {
+    public:
+        MetaTracker(
+                std::shared_ptr<celix_bundle_context_t> _cCtx,
+                std::string _serviceName,
+                std::vector<std::function<void(const ServiceTrackerInfo&)>> _onTrackerCreated,
+                std::vector<std::function<void(const ServiceTrackerInfo&)>> _onTrackerDestroyed) :
+                AbstractTracker{std::move(_cCtx)},
+                serviceName{std::move(_serviceName)},
+                onTrackerCreated{std::move(_onTrackerCreated)},
+                onTrackerDestroyed{std::move(_onTrackerDestroyed)} {}
+
+        ~MetaTracker() override {
+            close(); //if state is OPEN, queue stop tracker.
+            wait(); //if state is CLOSING, wait till closing is complete.
+        }
+
+        void open() override {
+            wait(); //if state is CLOSING, wait till tracker is closed
+            std::lock_guard<std::mutex> lck{mutex};
+            if (state == TrackerState::CLOSED) {
+                state = TrackerState::OPENING;
+
+                //NOTE the opts already configured the callbacks
+                trkId = celix_bundleContext_trackServiceTrackersAsync(
+                        cCtx.get(),
+                        serviceName.empty() ? nullptr : serviceName.c_str(),
+                        static_cast<void*>(this),
+                        [](void *handle, const celix_service_tracker_info_t *cInfo) {
+                            auto *trk = static_cast<MetaTracker *>(handle);
+                            ServiceTrackerInfo info{cInfo->serviceName, celix::Filter::wrap(cInfo->filter), cInfo->bundleId};
+                            for (const auto& cb : trk->onTrackerCreated) {
+                                cb(info);
+                            }
+                        },
+                        [](void *handle, const celix_service_tracker_info_t *cInfo) {
+                            auto *trk = static_cast<MetaTracker *>(handle);
+                            ServiceTrackerInfo info{cInfo->serviceName, celix::Filter::wrap(cInfo->filter), cInfo->bundleId};
+                            for (const auto& cb : trk->onTrackerDestroyed) {
+                                cb(info);
+                            }
+                        },
+                        static_cast<void*>(this),
+                        [](void *data) {
+                            auto *trk = static_cast<MetaTracker *>(data);
+                            std::lock_guard<std::mutex> callbackLock{trk->mutex};
+                            trk->state = TrackerState::OPEN;
+                        });
+            }
+        }
+
+        private:
+            const std::string serviceName;
+            const std::vector<std::function<void(const ServiceTrackerInfo&)>> onTrackerCreated; //TODO check if the callback are done on started/stopped or created/destroyed
+            const std::vector<std::function<void(const ServiceTrackerInfo&)>> onTrackerDestroyed;
+        };
+
+}
\ No newline at end of file
diff --git a/libs/framework/include/celix/dm/types.h b/libs/framework/include/celix/Utils.h
similarity index 81%
copy from libs/framework/include/celix/dm/types.h
copy to libs/framework/include/celix/Utils.h
index 8ccf87f..58824e1 100644
--- a/libs/framework/include/celix/dm/types.h
+++ b/libs/framework/include/celix/Utils.h
@@ -19,35 +19,12 @@
 
 #pragma once
 
-#include <map>
+
 #include <string>
-#include <list>
-#include <tuple>
-#include <typeinfo>
-#include <memory>
-#include <cxxabi.h>
 #include <string.h>
-#include "celix/dm/Properties.h"
-#include <stdlib.h>
 #include <iostream>
 
-namespace celix { namespace dm {
-    //forward declarations
-    class DependencyManager;
-
-    class BaseServiceDependency;
-
-    class BaseProvidedService;
-
-    template<typename T, typename I>
-    class ProvidedService;
-
-    template<class T, typename I>
-    class CServiceDependency;
-
-    template<class T, class I>
-    class ServiceDependency;
-
+namespace celix {
     /**
      * Returns the deferred type name for the template I
      */
@@ -88,4 +65,12 @@ namespace celix { namespace dm {
         return result;
     }
 
-}}
+    /**
+     * Returns the deferred type name for the template I if hte providedTypeName is empty.
+     * Else returns the providedTypeName
+     */
+    template<typename I>
+    std::string typeName(const std::string& providedTypeName) {
+        return providedTypeName.empty() ? celix::typeName<I>() : providedTypeName;
+    }
+}
diff --git a/libs/framework/include/celix/dm/Component.h b/libs/framework/include/celix/dm/Component.h
index c5d9c59..4101aab 100644
--- a/libs/framework/include/celix/dm/Component.h
+++ b/libs/framework/include/celix/dm/Component.h
@@ -19,13 +19,23 @@
 
 #pragma once
 
-#include "celix/dm/types.h"
-#include "dm_component.h"
-
 #include <map>
 #include <string>
 #include <vector>
 #include <atomic>
+#include <memory>
+#include <iostream>
+#include <type_traits>
+#include <algorithm>
+
+
+#include "dm_component.h"
+#include "celix/dm/types.h"
+#include "celix/dm/ServiceDependency.h"
+#include "celix/dm/ProvidedService.h"
+#include "celix_dependency_manager.h"
+
+
 
 namespace celix { namespace dm {
 
diff --git a/libs/framework/include/celix/dm/Component_Impl.h b/libs/framework/include/celix/dm/Component_Impl.h
index 6e0d8f3..6b55ad8 100644
--- a/libs/framework/include/celix/dm/Component_Impl.h
+++ b/libs/framework/include/celix/dm/Component_Impl.h
@@ -17,19 +17,6 @@
  * under the License.
  */
 
-#include "celix/dm/Component.h"
-#include "celix/dm/DependencyManager.h"
-#include "celix/dm/ServiceDependency.h"
-#include "celix/dm/ProvidedService.h"
-#include "celix_dependency_manager.h"
-
-#include <memory>
-#include <iostream>
-#include <iomanip>
-#include <type_traits>
-#include <algorithm>
-#include <atomic>
-
 using namespace celix::dm;
 
 inline void BaseComponent::runBuild() {
diff --git a/libs/framework/include/celix/dm/DependencyManager.h b/libs/framework/include/celix/dm/DependencyManager.h
index 6dc8a94..02cc472 100644
--- a/libs/framework/include/celix/dm/DependencyManager.h
+++ b/libs/framework/include/celix/dm/DependencyManager.h
@@ -21,7 +21,6 @@
 
 #include "celix/dm/types.h"
 #include "celix/dm/Component.h"
-#include "celix/dm/ServiceDependency.h"
 
 #include "bundle_context.h"
 #include "celix_bundle_context.h"
diff --git a/libs/framework/include/celix/dm/DependencyManager_Impl.h b/libs/framework/include/celix/dm/DependencyManager_Impl.h
index 3a05135..85a4fd4 100644
--- a/libs/framework/include/celix/dm/DependencyManager_Impl.h
+++ b/libs/framework/include/celix/dm/DependencyManager_Impl.h
@@ -17,7 +17,6 @@
  * under the License.
  */
 
-#include "DependencyManager.h"
 
 using namespace celix::dm;
 
diff --git a/libs/framework/include/celix/dm/Properties.h b/libs/framework/include/celix/dm/Properties.h
index 5c33885..e22d62d 100644
--- a/libs/framework/include/celix/dm/Properties.h
+++ b/libs/framework/include/celix/dm/Properties.h
@@ -19,9 +19,8 @@
 
 #pragma once
 
-#include <map>
-#include <string>
+#include "celix/Properties.h"
 
 namespace celix { namespace dm {
-    using Properties = std::map<std::string, std::string>;
+    using Properties = celix::Properties;
 }}
diff --git a/libs/framework/include/celix/dm/ProvidedService.h b/libs/framework/include/celix/dm/ProvidedService.h
index 76a4c2e..6723d37 100644
--- a/libs/framework/include/celix/dm/ProvidedService.h
+++ b/libs/framework/include/celix/dm/ProvidedService.h
@@ -22,7 +22,6 @@
 #include <string>
 #include <memory>
 
-#include "celix_api.h"
 #include "celix/dm/Properties.h"
 
 namespace celix { namespace dm {
diff --git a/libs/framework/include/celix/dm/ProvidedService_Impl.h b/libs/framework/include/celix/dm/ProvidedService_Impl.h
index 4cb9fbb..1035d18 100644
--- a/libs/framework/include/celix/dm/ProvidedService_Impl.h
+++ b/libs/framework/include/celix/dm/ProvidedService_Impl.h
@@ -43,8 +43,6 @@ inline void BaseProvidedService::runBuild() {
     if (!provideAddedToCmp) {
         //setup c properties
         celix_properties_t *cProperties = properties_create();
-        properties_set(cProperties, CELIX_FRAMEWORK_SERVICE_LANGUAGE,
-                       cppService ? CELIX_FRAMEWORK_SERVICE_CXX_LANGUAGE : CELIX_FRAMEWORK_SERVICE_C_LANGUAGE);
         for (const auto &pair : properties) {
             properties_set(cProperties, pair.first.c_str(), pair.second.c_str());
         }
diff --git a/libs/framework/include/celix/dm/ServiceDependency.h b/libs/framework/include/celix/dm/ServiceDependency.h
index 0de57e9..bf057e6 100644
--- a/libs/framework/include/celix/dm/ServiceDependency.h
+++ b/libs/framework/include/celix/dm/ServiceDependency.h
@@ -19,9 +19,6 @@
 
 #pragma once
 
-#include "dm_service_dependency.h"
-#include "celix/dm/types.h"
-
 #include <map>
 #include <string>
 #include <list>
@@ -30,10 +27,16 @@
 #include <iostream>
 #include <functional>
 #include <atomic>
+#include <vector>
+#include <cstring>
 
-namespace celix { namespace dm {
+#include "dm_service_dependency.h"
+#include "celix_constants.h"
+#include "celix_properties.h"
+#include "celix/Utils.h"
+#include "celix/dm/Properties.h"
 
-    class DependencyManager; //forward declaration
+namespace celix { namespace dm {
 
     enum class DependencyUpdateStrategy {
         suspend,
@@ -338,11 +341,9 @@ namespace celix { namespace dm {
          */
         ServiceDependency<T,I>& build();
     private:
-        bool addCxxLanguageFilter {true};
         std::string name {};
         std::string filter {};
         std::string versionRange {};
-        std::string modifiedFilter {};
 
         std::function<void(I* service, Properties&& properties)> setFp{nullptr};
         std::function<void(I* service, Properties&& properties)> addFp{nullptr};
diff --git a/libs/framework/include/celix/dm/ServiceDependency_Impl.h b/libs/framework/include/celix/dm/ServiceDependency_Impl.h
index a9dd5d2..70cb9ce 100644
--- a/libs/framework/include/celix/dm/ServiceDependency_Impl.h
+++ b/libs/framework/include/celix/dm/ServiceDependency_Impl.h
@@ -17,14 +17,6 @@
  * under the License.
  */
 
-#include <vector>
-#include <iostream>
-#include <cstring>
-#include "celix_constants.h"
-#include "celix_properties.h"
-#include "ServiceDependency.h"
-
-
 using namespace celix::dm;
 
 inline void BaseServiceDependency::runBuild() {
@@ -253,7 +245,7 @@ void ServiceDependency<T,I>::setupService() {
     std::string n = name;
 
     if (n.empty()) {
-        n = typeName<I>();
+        n = celix::typeName<I>();
         if (n.empty()) {
             std::cerr << "Error in service dependency. Type name cannot be inferred, using '<TYPE_NAME_ERROR>'. function: '" << __PRETTY_FUNCTION__ << "'\n";
             n = "<TYPE_NAME_ERROR>";
@@ -261,40 +253,7 @@ void ServiceDependency<T,I>::setupService() {
     }
 
     const char* v =  versionRange.empty() ? nullptr : versionRange.c_str();
-
-
-    if (this->addCxxLanguageFilter) {
-
-        char langFilter[128];
-        snprintf(langFilter, sizeof(langFilter), "(%s=%s)", CELIX_FRAMEWORK_SERVICE_LANGUAGE,
-                 CELIX_FRAMEWORK_SERVICE_CXX_LANGUAGE);
-
-        //setting modified filter. which is in a filter including a lang=c++
-        modifiedFilter = std::string{langFilter};
-        if (!filter.empty()) {
-            char needle[128];
-            snprintf(needle, sizeof(needle), "(%s=", CELIX_FRAMEWORK_SERVICE_LANGUAGE);
-            size_t langFilterPos = filter.find(needle);
-            if (langFilterPos != std::string::npos) {
-                //do nothing filter already contains a language part.
-            } else if (strncmp(filter.c_str(), "(&", 2) == 0 && filter[filter.size() - 1] == ')') {
-                modifiedFilter = filter.substr(0, filter.size() - 1); //remove closing ')'
-                modifiedFilter = modifiedFilter.append(langFilter);
-                modifiedFilter = modifiedFilter.append(")"); //add closing ')'
-            } else if (filter[0] == '(' && filter[filter.size() - 1] == ')') {
-                modifiedFilter = "(&";
-                modifiedFilter = modifiedFilter.append(filter);
-                modifiedFilter = modifiedFilter.append(langFilter);
-                modifiedFilter = modifiedFilter.append(")");
-            } else {
-                std::cerr << "Unexpected filter layout: '" << filter << "'\n";
-            }
-        }
-    } else {
-        this->modifiedFilter = this->filter;
-    }
-
-    celix_dmServiceDependency_setService(this->cServiceDependency(), n.c_str(), v, this->modifiedFilter.c_str());
+    celix_dmServiceDependency_setService(this->cServiceDependency(), n.c_str(), v, this->filter.c_str());
 }
 
 template<class T, class I>
diff --git a/libs/framework/include/celix/dm/types.h b/libs/framework/include/celix/dm/types.h
index 8ccf87f..a4a1944 100644
--- a/libs/framework/include/celix/dm/types.h
+++ b/libs/framework/include/celix/dm/types.h
@@ -27,13 +27,12 @@
 #include <memory>
 #include <cxxabi.h>
 #include <string.h>
-#include "celix/dm/Properties.h"
 #include <stdlib.h>
 #include <iostream>
 
 namespace celix { namespace dm {
     //forward declarations
-    class DependencyManager;
+//    class DependencyManager;
 
     class BaseServiceDependency;
 
@@ -48,44 +47,4 @@ namespace celix { namespace dm {
     template<class T, class I>
     class ServiceDependency;
 
-    /**
-     * Returns the deferred type name for the template I
-     */
-    template<typename INTERFACE_TYPENAME>
-    std::string typeName() {
-        std::string result;
-
-#ifdef __GXX_RTTI
-        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;
-    }
-
 }}
diff --git a/libs/framework/include/celix_api.h b/libs/framework/include/celix_api.h
index c66bae1..123f74a 100644
--- a/libs/framework/include/celix_api.h
+++ b/libs/framework/include/celix_api.h
@@ -46,8 +46,8 @@
 #include "celix_framework_factory.h"
 
 #ifdef __cplusplus
-#include "celix/dm/DependencyManager.h"
+#include "celix/BundleActivator.h"
+#include "celix/BundleContext.h"
 #endif
 
-
 #endif //CELIX_CELIX_API_H_
diff --git a/libs/framework/include/celix_bundle_activator.h b/libs/framework/include/celix_bundle_activator.h
index ab02ed2..aaaca2e 100644
--- a/libs/framework/include/celix_bundle_activator.h
+++ b/libs/framework/include/celix_bundle_activator.h
@@ -140,74 +140,6 @@ celix_status_t celix_bundleActivator_destroy(void *userData, celix_bundle_contex
 #ifdef __cplusplus
 }
 
-
-/**
- * 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:
- * - bundleActivator_create which allocates a pointer to the provided type.
- * - bundleActivator_start/stop which will call the respectively provided typed start/stop functions.
- * - bundleActivator_destroy will free the allocated for the provided type.
- *
- * @param type The activator type (e.g. 'ShellActivator'). A type which should have a constructor with a single arugment of std::shared_ptr<DependencyManager>.
- */
-#define CELIX_GEN_CXX_BUNDLE_ACTIVATOR(actType)                                                                        \
-                                                                                                                       \
-namespace /*anon*/ {                                                                                                   \
-struct BundleActivatorData {                                                                                           \
-    std::shared_ptr<DependencyManager> mng{};                                                                          \
-    std::unique_ptr<actType> activator{};                                                                              \
-};                                                                                                                     \
-}                                                                                                                      \
-                                                                                                                       \
-extern "C" celix_status_t bundleActivator_create(celix_bundle_context_t *context, void** userData) {                   \
-    int status = CELIX_SUCCESS;                                                                                        \
-                                                                                                                       \
-    BundleActivatorData* data = nullptr;                                                                               \
-    data = new (std::nothrow) BundleActivatorData{};                                                                   \
-    if (data != nullptr) {                                                                                             \
-        data->mng = std::shared_ptr<celix::dm::DependencyManager>{new (std::nothrow) celix::dm::DependencyManager{context}};          \
-    }                                                                                                                  \
-                                                                                                                       \
-    if (data == nullptr || data->mng == nullptr) {                                                                     \
-        status = CELIX_ENOMEM;                                                                                         \
-        if (data != nullptr) {                                                                                         \
-            delete data;                                                                                               \
-        }                                                                                                              \
-        *userData = nullptr;                                                                                           \
-    } else {                                                                                                           \
-        *userData = data;                                                                                              \
-    }                                                                                                                  \
-    return status;                                                                                                     \
-}                                                                                                                      \
-                                                                                                                       \
-extern "C" celix_status_t bundleActivator_start(void *userData, celix_bundle_context_t *) {                            \
-    auto* data = static_cast<BundleActivatorData*>(userData);                                                          \
-    if (data != nullptr) {                                                                                             \
-        data->activator = std::unique_ptr<actType>{new actType{data->mng}};                                            \
-        data->mng->start();                                                                                            \
-    }                                                                                                                  \
-    return CELIX_SUCCESS;                                                                                              \
-}                                                                                                                      \
-                                                                                                                       \
-extern "C" celix_status_t bundleActivator_stop(void *userData, celix_bundle_context_t*) {                              \
-    auto* data = static_cast<BundleActivatorData*>(userData);                                                          \
-    if (data != nullptr) {                                                                                             \
-        data->mng->stop();                                                                                             \
-        data->activator = nullptr;                                                                                     \
-        data->mng = nullptr;                                                                                           \
-    }                                                                                                                  \
-    return CELIX_SUCCESS;                                                                                              \
-}                                                                                                                      \
-                                                                                                                       \
-extern "C" celix_status_t bundleActivator_destroy(void *userData, celix_bundle_context_t*) {                           \
-    auto* data = static_cast<BundleActivatorData*>(userData);                                                          \
-    delete data;                                                                                                       \
-    return CELIX_SUCCESS;                                                                                              \
-}                                                                                                                      \
-
-
 #endif
 
 #endif /* CELIX_BUNDLE_ACTIVATOR_H_ */
\ No newline at end of file
diff --git a/libs/framework/include/celix_bundle_context.h b/libs/framework/include/celix_bundle_context.h
index b99c8b5..d113f21 100644
--- a/libs/framework/include/celix_bundle_context.h
+++ b/libs/framework/include/celix_bundle_context.h
@@ -315,14 +315,15 @@ typedef struct celix_service_filter_options {
     const char* filter OPTS_INIT;
 
     /**
-     * The optional service language to filter for. If this is NULL or "" the C language will be used.
+     * @deprecated This value is not used any more. If a service language filter is still required add it to the
+     * filter.
      */
     const char* serviceLanguage OPTS_INIT;
 
 
     /**
-     * Whether to ignore (not filter for) the service.lang property.
-     * If this is set the serviceLanguage field is ignored and the (service.lang=<>) part is not added tot he filter
+     * @deprecated This value is not used any more. If a service language filter is still required add it to the
+     * filter.
      */
     bool ignoreServiceLanguage OPTS_INIT;
 } celix_service_filter_options_t;
@@ -1055,7 +1056,8 @@ typedef struct celix_service_tracker_info {
     const char *serviceName;
 
     /**
-     * The service language filter attribute parsed from the service filter. Can be null
+     * @deprecated
+     * Deprecated. the value will be NULL.
      */
     const char *serviceLanguage;
 
@@ -1183,7 +1185,16 @@ double celix_bundleContext_getPropertyAsDouble(celix_bundle_context_t *ctx, cons
  */
 bool celix_bundleContext_getPropertyAsBool(celix_bundle_context_t *ctx, const char *key, bool defaultValue);
 
-//TODO getPropertyAs for int, uint, ulong, bool, etc
+/**
+ * Logs a message to the Celix framework logger using the provided log level.
+ */
+void celix_bundleContext_log(celix_bundle_context_t *ctx, celix_log_level_e level, const char* format, ...);
+
+/**
+ * Logs a message to Celix framework logger using the provided log level.
+ */
+void celix_bundleContext_vlog(celix_bundle_context_t *ctx, celix_log_level_e level, const char *format, va_list formatArgs);
+
 
 #undef OPTS_INIT
 
diff --git a/libs/framework/include/celix_constants.h b/libs/framework/include/celix_constants.h
index 17fb61f..4967fad 100644
--- a/libs/framework/include/celix_constants.h
+++ b/libs/framework/include/celix_constants.h
@@ -26,64 +26,62 @@
 extern "C" {
 #endif
 
-static const char *const OSGI_FRAMEWORK_OBJECTCLASS = "objectClass";
-static const char *const OSGI_FRAMEWORK_SERVICE_ID = "service.id";
-static const char *const OSGI_FRAMEWORK_SERVICE_PID = "service.pid";
+#define OSGI_FRAMEWORK_OBJECTCLASS "objectClass"
+#define OSGI_FRAMEWORK_SERVICE_ID "service.id"
+#define OSGI_FRAMEWORK_SERVICE_PID "service.pid"
 
 /**
  * Service property (named "service.ranking") identifying a service's ranking number (of type long).
  * The default ranking is 0. A service with a ranking of LONG_MAX is very likely to be returned as the default service, whereas a service with a ranking of LONG_MIN is very unlikely to be returned.
  * If the supplied property value cannot converted to long, a ranking value of 0 is used.
  */
-static const char *const OSGI_FRAMEWORK_SERVICE_RANKING = "service.ranking";
+#define OSGI_FRAMEWORK_SERVICE_RANKING "service.ranking"
 
-static const char *const CELIX_FRAMEWORK_SERVICE_VERSION = "service.version";
-static const char *const CELIX_FRAMEWORK_SERVICE_LANGUAGE = "service.lang";
-static const char *const CELIX_FRAMEWORK_SERVICE_C_LANGUAGE = "C";
-static const char *const CELIX_FRAMEWORK_SERVICE_CXX_LANGUAGE = "C++";
-static const char *const CELIX_FRAMEWORK_SERVICE_SHARED_LANGUAGE = "shared"; //e.g. marker services
+#define CELIX_FRAMEWORK_SERVICE_VERSION "service.version"
 
-static const char *const OSGI_FRAMEWORK_BUNDLE_ACTIVATOR = "Bundle-Activator";
-
-static const char *const OSGI_FRAMEWORK_BUNDLE_ACTIVATOR_CREATE = "celix_bundleActivator_create";
-static const char *const OSGI_FRAMEWORK_BUNDLE_ACTIVATOR_START = "celix_bundleActivator_start";
-static const char *const OSGI_FRAMEWORK_BUNDLE_ACTIVATOR_STOP = "celix_bundleActivator_stop";
-static const char *const OSGI_FRAMEWORK_BUNDLE_ACTIVATOR_DESTROY = "celix_bundleActivator_destroy";
-
-static const char *const OSGI_FRAMEWORK_DEPRECATED_BUNDLE_ACTIVATOR_CREATE = "bundleActivator_create";
-static const char *const OSGI_FRAMEWORK_DEPRECATED_BUNDLE_ACTIVATOR_START = "bundleActivator_start";
-static const char *const OSGI_FRAMEWORK_DEPRECATED_BUNDLE_ACTIVATOR_STOP = "bundleActivator_stop";
-static const char *const OSGI_FRAMEWORK_DEPRECATED_BUNDLE_ACTIVATOR_DESTROY = "bundleActivator_destroy";
+/**
+ * The service language property and values are deprecated
+ */
+//#define CELIX_FRAMEWORK_SERVICE_LANGUAGE "service.lang"
+//#define CELIX_FRAMEWORK_SERVICE_C_LANGUAGE "C"
+//#define CELIX_FRAMEWORK_SERVICE_CXX_LANGUAGE "C++"
+//#define CELIX_FRAMEWORK_SERVICE_SHARED_LANGUAGE "shared" //e.g. marker services
 
+#define OSGI_FRAMEWORK_BUNDLE_ACTIVATOR "Bundle-Activator"
 
-static const char *const OSGI_FRAMEWORK_BUNDLE_DM_ACTIVATOR_CREATE = "dm_create";
-static const char *const OSGI_FRAMEWORK_BUNDLE_DM_ACTIVATOR_INIT = "dm_init";
-static const char *const OSGI_FRAMEWORK_BUNDLE_DM_ACTIVATOR_DESTROY = "dm_destroy";
+#define OSGI_FRAMEWORK_BUNDLE_ACTIVATOR_CREATE "celix_bundleActivator_create"
+#define OSGI_FRAMEWORK_BUNDLE_ACTIVATOR_START "celix_bundleActivator_start"
+#define OSGI_FRAMEWORK_BUNDLE_ACTIVATOR_STOP "celix_bundleActivator_stop"
+#define OSGI_FRAMEWORK_BUNDLE_ACTIVATOR_DESTROY "celix_bundleActivator_destroy"
 
+#define OSGI_FRAMEWORK_DEPRECATED_BUNDLE_ACTIVATOR_CREATE "bundleActivator_create"
+#define OSGI_FRAMEWORK_DEPRECATED_BUNDLE_ACTIVATOR_START "bundleActivator_start"
+#define OSGI_FRAMEWORK_DEPRECATED_BUNDLE_ACTIVATOR_STOP "bundleActivator_stop"
+#define OSGI_FRAMEWORK_DEPRECATED_BUNDLE_ACTIVATOR_DESTROY "bundleActivator_destroy"
 
-static const char *const OSGI_FRAMEWORK_BUNDLE_SYMBOLICNAME = "Bundle-SymbolicName";
-static const char *const OSGI_FRAMEWORK_BUNDLE_VERSION = "Bundle-Version";
-static const char *const OSGI_FRAMEWORK_PRIVATE_LIBRARY = "Private-Library";
-static const char *const OSGI_FRAMEWORK_EXPORT_LIBRARY = "Export-Library";
-static const char *const OSGI_FRAMEWORK_IMPORT_LIBRARY = "Import-Library";
+#define OSGI_FRAMEWORK_BUNDLE_SYMBOLICNAME "Bundle-SymbolicName"
+#define OSGI_FRAMEWORK_BUNDLE_VERSION "Bundle-Version"
+#define OSGI_FRAMEWORK_PRIVATE_LIBRARY "Private-Library"
+#define OSGI_FRAMEWORK_EXPORT_LIBRARY "Export-Library"
+#define OSGI_FRAMEWORK_IMPORT_LIBRARY "Import-Library"
 
-static const char *const OSGI_FRAMEWORK_FRAMEWORK_STORAGE = "org.osgi.framework.storage";
-static const char *const OSGI_FRAMEWORK_STORAGE_USE_TMP_DIR = "org.osgi.framework.storage.use.tmp.dir";
-static const char *const OSGI_FRAMEWORK_FRAMEWORK_STORAGE_CLEAN_NAME = "org.osgi.framework.storage.clean";
-static const bool        OSGI_FRAMEWORK_FRAMEWORK_STORAGE_CLEAN_DEFAULT = true;
-static const char *const OSGI_FRAMEWORK_FRAMEWORK_UUID = "org.osgi.framework.uuid";
+#define OSGI_FRAMEWORK_FRAMEWORK_STORAGE "org.osgi.framework.storage"
+#define OSGI_FRAMEWORK_STORAGE_USE_TMP_DIR "org.osgi.framework.storage.use.tmp.dir"
+#define OSGI_FRAMEWORK_FRAMEWORK_STORAGE_CLEAN_NAME "org.osgi.framework.storage.clean"
+#define OSGI_FRAMEWORK_FRAMEWORK_STORAGE_CLEAN_DEFAULT true
+#define OSGI_FRAMEWORK_FRAMEWORK_UUID "org.osgi.framework.uuid"
 
-static const char *const CELIX_BUNDLES_PATH_NAME = "CELIX_BUNDLES_PATH";
-static const char *const CELIX_BUNDLES_PATH_DEFAULT = "bundles";
+#define CELIX_BUNDLES_PATH_NAME "CELIX_BUNDLES_PATH"
+#define CELIX_BUNDLES_PATH_DEFAULT "bundles"
 
-static const char *const CELIX_LOAD_BUNDLES_WITH_NODELETE = "CELIX_LOAD_BUNDLES_WITH_NODELETE";
+#define CELIX_LOAD_BUNDLES_WITH_NODELETE "CELIX_LOAD_BUNDLES_WITH_NODELETE"
 
 /**
  * The path used getting entries from the framework bundle.
  * Normal bundles have an archive directory.
  * For the celix framework by default the working directory is used, with this configuration this can be changed.
  */
-static const char *const CELIX_SYSTEM_BUNDLE_ARCHIVE_PATH = "CELIX_SYSTEM_BUNDLE_ARCHIVE_PATH";
+#define CELIX_SYSTEM_BUNDLE_ARCHIVE_PATH "CELIX_SYSTEM_BUNDLE_ARCHIVE_PATH"
 
 
 #define CELIX_AUTO_START_0 "CELIX_AUTO_START_0"
diff --git a/libs/framework/include/celix_log.h b/libs/framework/include/celix_log.h
index 2a56695..c170342 100644
--- a/libs/framework/include/celix_log.h
+++ b/libs/framework/include/celix_log.h
@@ -64,6 +64,8 @@ void framework_log(celix_framework_logger_t* logger, celix_log_level_e level, co
 void framework_logCode(celix_framework_logger_t* logger, celix_log_level_e level, const char *func, const char *file, int line,
                   celix_status_t code, const char *format, ...);
 
+void celix_framework_vlog(celix_framework_logger_t* logger, celix_log_level_e level, celix_status_t *optionalStatus, const char* file, const char* function, int line, const char* format, va_list args);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libs/framework/include/dm_component.h b/libs/framework/include/dm_component.h
index 84cb3d7..92b5b93 100644
--- a/libs/framework/include/dm_component.h
+++ b/libs/framework/include/dm_component.h
@@ -63,8 +63,7 @@ void component_destroy(celix_dm_component_t *component) CELIX_DEPRECATED_ATTR;
 
 
 /**
- * Specify if a default 'service.lang=C' should be added to the properties of interfaces if no 'service.lang' has been
- * provided. Default is false. Note that this should be set before using component_addInterface.
+ * @deprecated Deprecated. not used ny mowre
  */
 celix_status_t component_setCLanguageProperty(celix_dm_component_t *component, bool setCLangProp) CELIX_DEPRECATED_ATTR;
 
diff --git a/libs/framework/include/service_registry.h b/libs/framework/include/service_registry.h
index 275f707..87cd6c9 100644
--- a/libs/framework/include/service_registry.h
+++ b/libs/framework/include/service_registry.h
@@ -164,9 +164,7 @@ char* celix_serviceRegistry_createFilterFor(
         celix_service_registry_t* registry,
         const char* serviceName,
         const char* versionRange,
-        const char* additionalFilter,
-        const char* serviceLanguage,
-        bool ignoreServiceLanguage);
+        const char* additionalFilter);
 
 /**
  * Find services and return a array list of service ids (long).
diff --git a/libs/framework/src/bundle_context.c b/libs/framework/src/bundle_context.c
index 2f1473d..819c8df 100644
--- a/libs/framework/src/bundle_context.c
+++ b/libs/framework/src/bundle_context.c
@@ -23,6 +23,7 @@
 #include <utils.h>
 #include <assert.h>
 #include <unistd.h>
+#include <stdarg.h>
 
 #include "celix_utils.h"
 #include "celix_constants.h"
@@ -501,8 +502,6 @@ static long celix_bundleContext_registerServiceWithOptionsInternal(bundle_contex
     if (opts->serviceVersion != NULL && strncmp("", opts->serviceVersion, 1) != 0) {
         celix_properties_set(props, CELIX_FRAMEWORK_SERVICE_VERSION, opts->serviceVersion);
     }
-    const char *lang = opts->serviceLanguage != NULL && strncmp("", opts->serviceLanguage, 1) != 0 ? opts->serviceLanguage : CELIX_FRAMEWORK_SERVICE_C_LANGUAGE;
-    celix_properties_set(props, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang);
 
     long svcId = -1;
     if (!async && celix_framework_isCurrentThreadTheEventLoop(ctx->framework)) {
@@ -1425,7 +1424,7 @@ long celix_bundleContext_findService(celix_bundle_context_t *ctx, const char *se
 
 long celix_bundleContext_findServiceWithOptions(celix_bundle_context_t *ctx, const celix_service_filter_options_t *opts) {
     long result = -1L;
-    char* filter = celix_serviceRegistry_createFilterFor(ctx->framework->registry, opts->serviceName, opts->versionRange, opts->filter, opts->serviceLanguage, opts->ignoreServiceLanguage);
+    char* filter = celix_serviceRegistry_createFilterFor(ctx->framework->registry, opts->serviceName, opts->versionRange, opts->filter);
     if (filter != NULL) {
         celix_array_list_t *svcIds = celix_serviceRegisrty_findServices(ctx->framework->registry, filter);
         if (svcIds != NULL && celix_arrayList_size(svcIds) > 0) {
@@ -1448,7 +1447,7 @@ celix_array_list_t* celix_bundleContext_findServices(celix_bundle_context_t *ctx
 
 celix_array_list_t* celix_bundleContext_findServicesWithOptions(celix_bundle_context_t *ctx, const celix_service_filter_options_t *opts) {
     celix_array_list_t* result = NULL;
-    char* filter = celix_serviceRegistry_createFilterFor(ctx->framework->registry, opts->serviceName, opts->versionRange, opts->filter, opts->serviceLanguage, opts->ignoreServiceLanguage);
+    char* filter = celix_serviceRegistry_createFilterFor(ctx->framework->registry, opts->serviceName, opts->versionRange, opts->filter);
     if (filter != NULL) {
         result = celix_serviceRegisrty_findServices(ctx->framework->registry, filter);
         free(filter);
@@ -1470,7 +1469,6 @@ static celix_status_t bundleContext_callServicedTrackerTrackerCallback(void *han
             trkInfo.bundleId = celix_bundle_getId(bnd);
             trkInfo.filter = celix_filter_create(info->filter);
             trkInfo.serviceName = celix_filter_findAttribute(trkInfo.filter, OSGI_FRAMEWORK_OBJECTCLASS);
-            trkInfo.serviceLanguage = celix_filter_findAttribute(trkInfo.filter, CELIX_FRAMEWORK_SERVICE_LANGUAGE);
             const char *filterSvcName = celix_filter_findAttribute(trkInfo.filter, OSGI_FRAMEWORK_OBJECTCLASS);
 
             bool match = entry->serviceName == NULL || (filterSvcName != NULL && strncmp(filterSvcName, entry->serviceName, 1024*1024) == 0);
@@ -1649,3 +1647,14 @@ char* celix_bundleContext_getBundleSymbolicName(celix_bundle_context_t *ctx, lon
     celix_framework_useBundle(ctx->framework, false, bndId, &name, celix_bundleContext_getBundleSymbolicNameCallback);
     return name;
 }
+
+void celix_bundleContext_log(celix_bundle_context_t *ctx, celix_log_level_e level, const char* format, ...) {
+    va_list args;
+    va_start(args, format);
+    celix_bundleContext_vlog(ctx, level, format, args);
+    va_end(args);
+}
+
+void celix_bundleContext_vlog(celix_bundle_context_t *ctx, celix_log_level_e level, const char *format, va_list formatArgs) {
+    celix_framework_vlog(ctx->framework->logger, level, NULL, NULL, NULL, 0, format, formatArgs);
+}
\ No newline at end of file
diff --git a/libs/framework/src/celix_log.c b/libs/framework/src/celix_log.c
index 012283d..e959d35 100644
--- a/libs/framework/src/celix_log.c
+++ b/libs/framework/src/celix_log.c
@@ -103,7 +103,7 @@ void celix_frameworkLogger_setLogCallback(celix_framework_logger_t* logger, void
 }
 
 
-static void vlog(celix_framework_logger_t* logger, celix_log_level_e level, celix_status_t *optionalStatus, const char* file, const char* function, int line, const char* format, va_list args) {
+void celix_framework_vlog(celix_framework_logger_t* logger, celix_log_level_e level, celix_status_t *optionalStatus, const char* file, const char* function, int line, const char* format, va_list args) {
     if (level == CELIX_LOG_LEVEL_DISABLED) {
         return;
     }
@@ -130,13 +130,13 @@ static void vlog(celix_framework_logger_t* logger, celix_log_level_e level, celi
 void framework_log(celix_framework_logger_t*  logger, celix_log_level_e level, const char *func, const char *file, int line, const char *format, ...) {
     va_list args;
     va_start(args, format);
-    vlog(logger, level, NULL, file, func, line, format, args);
+    celix_framework_vlog(logger, level, NULL, file, func, line, format, args);
     va_end(args);
 }
 
 void framework_logCode(celix_framework_logger_t*  logger, celix_log_level_e level, const char *func, const char *file, int line, celix_status_t code, const char *format, ...) {
     va_list args;
     va_start(args, format);
-    vlog(logger, level, &code, file, func, line, format, args);
+    celix_framework_vlog(logger, level, &code, file, func, line, format, args);
     va_end(args);
 }
\ No newline at end of file
diff --git a/libs/framework/src/dm_component_impl.c b/libs/framework/src/dm_component_impl.c
index b76f8e9..5077b2b 100644
--- a/libs/framework/src/dm_component_impl.c
+++ b/libs/framework/src/dm_component_impl.c
@@ -50,8 +50,6 @@ struct celix_dm_component_struct {
     bool isStarted;
     bool active;
 
-    bool setCLanguageProperty;
-
     hash_map_pt dependencyEvents; //protected by mutex
 
     dm_executor_pt executor;
@@ -152,8 +150,6 @@ celix_dm_component_t* celix_dmComponent_create(bundle_context_t *context, const
     component->isStarted = false;
     component->active = false;
 
-    component->setCLanguageProperty = false;
-
     component->dependencyEvents = hashMap_create(NULL, NULL, NULL, NULL);
 
     component->executor = NULL;
@@ -360,7 +356,7 @@ celix_status_t component_setCLanguageProperty(celix_dm_component_t *component, b
 }
 
 celix_status_t celix_dmComponent_setCLanguageProperty(celix_dm_component_t *component, bool setCLangProp) {
-    component->setCLanguageProperty = setCLangProp;
+    //nop
     return CELIX_SUCCESS;
 }
 
@@ -382,10 +378,6 @@ celix_status_t celix_dmComponent_addInterface(celix_dm_component_t *component, c
         celix_properties_set(properties, CELIX_FRAMEWORK_SERVICE_VERSION, serviceVersion);
     }
 
-    if (component->setCLanguageProperty && properties_get(properties, CELIX_FRAMEWORK_SERVICE_LANGUAGE) == NULL) { //always set default lang to C
-        celix_properties_set(properties, CELIX_FRAMEWORK_SERVICE_LANGUAGE, CELIX_FRAMEWORK_SERVICE_C_LANGUAGE);
-    }
-
     celix_properties_set(properties, CELIX_DM_COMPONENT_UUID, (char*)component->id);
 
     if (interface && name) {
@@ -1256,7 +1248,6 @@ static celix_status_t component_registerServices(celix_dm_component_t *component
                 opts.properties = regProps;
                 opts.svc = (void*)interface->service;
                 opts.serviceName = interface->serviceName;
-                opts.serviceLanguage = celix_properties_get(regProps, CELIX_FRAMEWORK_SERVICE_LANGUAGE, NULL);
                 interface->svcId = celix_bundleContext_registerServiceWithOptions(component->context, &opts);
             }
         }
diff --git a/libs/framework/src/dm_service_dependency.c b/libs/framework/src/dm_service_dependency.c
index 52118d2..b98760d 100644
--- a/libs/framework/src/dm_service_dependency.c
+++ b/libs/framework/src/dm_service_dependency.c
@@ -214,23 +214,6 @@ celix_status_t celix_dmServiceDependency_setService(celix_dm_service_dependency_
 			arrayList_add(filterElements, strdup(filter));
 		}
 
-
-
-        bool needLangFilter = true;
-		if (filter != NULL) {
-            char needle[128];
-            snprintf(needle, sizeof(needle), "(%s=", CELIX_FRAMEWORK_SERVICE_LANGUAGE);
-            if (strstr(filter, needle) != NULL) {
-                needLangFilter = false;
-            }
-        }
-
-        if (needLangFilter && dependency->addCLanguageFilter) {
-			char langFilter[128];
-			snprintf(langFilter, sizeof(langFilter), "(%s=%s)", CELIX_FRAMEWORK_SERVICE_LANGUAGE, CELIX_FRAMEWORK_SERVICE_C_LANGUAGE);
-            arrayList_add(filterElements, strdup(langFilter));
-		}
-
 		if (arrayList_size(filterElements) > 0) {
 			array_list_iterator_pt filterElementsIter = arrayListIterator_create(filterElements);
 
diff --git a/libs/framework/src/service_registry.c b/libs/framework/src/service_registry.c
index 1b2924c..5953980 100644
--- a/libs/framework/src/service_registry.c
+++ b/libs/framework/src/service_registry.c
@@ -921,14 +921,9 @@ static inline void celix_waitAndDestroyServiceListener(celix_service_registry_se
     free(entry);
 }
 
-char* celix_serviceRegistry_createFilterFor(celix_service_registry_t* registry, const char* serviceName, const char* versionRangeStr, const char* additionalFilterIn, const char* lang, bool ignoreServiceLanguage) {
+char* celix_serviceRegistry_createFilterFor(celix_service_registry_t* registry, const char* serviceName, const char* versionRangeStr, const char* additionalFilterIn) {
     char* filter = NULL;
 
-    //setting lang
-    if (lang == NULL || strncmp("", lang, 1) == 0) {
-        lang = CELIX_FRAMEWORK_SERVICE_C_LANGUAGE;
-    }
-
     if (serviceName == NULL) {
         serviceName = "*";
     }
@@ -952,26 +947,14 @@ char* celix_serviceRegistry_createFilterFor(celix_service_registry_t* registry,
     }
 
     //setting filter
-    if (ignoreServiceLanguage) {
-        if (additionalFilterIn != NULL && versionRange != NULL) {
-            asprintf(&filter, "(&(%s=%s)%s%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, versionRange, additionalFilterIn);
-        } else if (versionRange != NULL) {
-            asprintf(&filter, "(&(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, versionRange);
-        } else if (additionalFilterIn != NULL) {
-            asprintf(&filter, "(&(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, additionalFilterIn);
-        } else {
-            asprintf(&filter, "(&(%s=%s))", OSGI_FRAMEWORK_OBJECTCLASS, serviceName);
-        }
+    if (additionalFilterIn != NULL && versionRange != NULL) {
+        asprintf(&filter, "(&(%s=%s)%s%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, versionRange, additionalFilterIn);
+    } else if (versionRange != NULL) {
+        asprintf(&filter, "(&(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, versionRange);
+    } else if (additionalFilterIn != NULL) {
+        asprintf(&filter, "(&(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, additionalFilterIn);
     } else {
-        if (additionalFilterIn != NULL && versionRange != NULL) {
-            asprintf(&filter, "(&(%s=%s)(%s=%s)%s%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang, versionRange, additionalFilterIn);
-        } else if (versionRange != NULL) {
-            asprintf(&filter, "(&(%s=%s)(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang, versionRange);
-        } else if (additionalFilterIn != NULL) {
-            asprintf(&filter, "(&(%s=%s)(%s=%s)%s)", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang, additionalFilterIn);
-        } else {
-            asprintf(&filter, "(&(%s=%s)(%s=%s))", OSGI_FRAMEWORK_OBJECTCLASS, serviceName, CELIX_FRAMEWORK_SERVICE_LANGUAGE, lang);
-        }
+        asprintf(&filter, "(&(%s=%s))", OSGI_FRAMEWORK_OBJECTCLASS, serviceName);
     }
 
     if (versionRange != NULL){
diff --git a/libs/framework/src/service_tracker.c b/libs/framework/src/service_tracker.c
index 4a47397..f28d503 100644
--- a/libs/framework/src/service_tracker.c
+++ b/libs/framework/src/service_tracker.c
@@ -658,7 +658,7 @@ celix_service_tracker_t* celix_serviceTracker_createWithOptions(
 
             tracker->listener.handle = tracker;
             tracker->listener.serviceChanged = (void *) serviceTracker_serviceChanged;
-            tracker->filter = celix_serviceRegistry_createFilterFor(ctx->framework->registry, opts->filter.serviceName, opts->filter.versionRange, opts->filter.filter, opts->filter.serviceLanguage, opts->filter.ignoreServiceLanguage);
+            tracker->filter = celix_serviceRegistry_createFilterFor(ctx->framework->registry, opts->filter.serviceName, opts->filter.versionRange, opts->filter.filter);
 
             if (tracker->filter == NULL) {
                 framework_log(tracker->context->framework->logger, CELIX_LOG_LEVEL_ERROR, __FUNCTION__, __BASE_FILE__, __LINE__,
diff --git a/libs/utils/src/filter.c b/libs/utils/src/filter.c
index c07b54e..615d1fb 100644
--- a/libs/utils/src/filter.c
+++ b/libs/utils/src/filter.c
@@ -714,7 +714,7 @@ const char* celix_filter_findAttribute(const celix_filter_t *filter, const char
                 }
             }
         } else if (strncmp(filter->attribute, attribute, 1024 * 1024) == 0) {
-            result = filter->value;
+            result = filter->operand == CELIX_FILTER_OPERAND_PRESENT ? "*" : filter->value;
         }
     }
     return result;
diff --git a/libs/utils/src/properties.c b/libs/utils/src/properties.c
index caf0769..c735a76 100644
--- a/libs/utils/src/properties.c
+++ b/libs/utils/src/properties.c
@@ -346,7 +346,7 @@ void celix_properties_store(celix_properties_t *properties, const char *filename
 
 celix_properties_t* celix_properties_copy(const celix_properties_t *properties) {
     celix_properties_t *copy = celix_properties_create();
-    if (copy != NULL) {
+    if (properties != NULL) {
         hash_map_iterator_t iter = hashMapIterator_construct((hash_map_t*)properties);
         while (hashMapIterator_hasNext(&iter)) {
             hash_map_entry_pt entry = hashMapIterator_nextEntry(&iter);