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 2023/04/05 22:03:06 UTC

[celix] branch feature/cap_req_model_lib created (now 52b95af3)

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

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


      at 52b95af3 Add rcm lib with base requirement-capability-model implementation

This branch includes the following new commits:

     new 52b95af3 Add rcm lib with base requirement-capability-model implementation

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



[celix] 01/01: Add rcm lib with base requirement-capability-model implementation

Posted by pn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 52b95af3157b678c6cf21f0929a2de6f11e909be
Author: Pepijn Noltes <pe...@gmail.com>
AuthorDate: Thu Apr 6 00:02:44 2023 +0200

    Add rcm lib with base requirement-capability-model implementation
---
 CMakeLists.txt                                     |   2 +-
 libs/CMakeLists.txt                                |   1 +
 libs/{ => rcm}/CMakeLists.txt                      |  38 ++--
 libs/rcm/README.md                                 |  44 +++++
 libs/rcm/diagrams/logical-req-cap-model.png        | Bin 0 -> 4927 bytes
 libs/rcm/diagrams/logical-req-cap-model.puml       |  23 +++
 libs/{ => rcm/gtest}/CMakeLists.txt                |  34 ++--
 .../src/RequirementCapabilityModelTestSuite.cc     | 217 +++++++++++++++++++++
 libs/rcm/include/celix_capability.h                |  73 +++++++
 libs/rcm/include/celix_rcm_err.h                   |  62 ++++++
 libs/rcm/include/celix_rcm_types.h                 |  42 ++++
 libs/rcm/include/celix_requirement.h               |  81 ++++++++
 libs/rcm/include/celix_resource.h                  |  62 ++++++
 libs/rcm/src/celix_capability.c                    | 171 ++++++++++++++++
 libs/rcm/src/celix_rcm_err.c                       | 105 ++++++++++
 libs/rcm/src/celix_rcm_err_private.h               |  42 ++++
 libs/rcm/src/celix_requirement.c                   | 182 +++++++++++++++++
 libs/rcm/src/celix_resource.c                      | 183 +++++++++++++++++
 18 files changed, 1318 insertions(+), 44 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 184b90d2..4512800e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -48,7 +48,7 @@ endif ()
 if (NOT TARGET CURL::libcurl)
     #Note more recent curl will create CURL::libcurl target
     message("Note CURL::libcurl target not created by find_package(CURL). Creating CURL::libcurl Target.")
-    add_library(CURL::libcurl SHARED IMPORTED)
+    add_library(CURL::libcurl SHARED IMPORTED libs/rcm/include/celix_requirement.h libs/rcm/include/celix_capability.h)
     set_target_properties(CURL::libcurl PROPERTIES
             IMPORTED_LOCATION "${CURL_LIBRARIES}"
             INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIRS}"
diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt
index feee84b7..20b51203 100644
--- a/libs/CMakeLists.txt
+++ b/libs/CMakeLists.txt
@@ -22,6 +22,7 @@ add_subdirectory(dfi)
 add_subdirectory(etcdlib)
 add_subdirectory(promises)
 add_subdirectory(pushstreams)
+add_subdirectory(rcm)
 
 add_subdirectory(framework)
 
diff --git a/libs/CMakeLists.txt b/libs/rcm/CMakeLists.txt
similarity index 59%
copy from libs/CMakeLists.txt
copy to libs/rcm/CMakeLists.txt
index feee84b7..bc812dc1 100644
--- a/libs/CMakeLists.txt
+++ b/libs/rcm/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,27 +15,19 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#utils, dfi and etcdlib are standalone
-#(e.g. no dependency on celix framework
-add_subdirectory(utils)
-add_subdirectory(dfi)
-add_subdirectory(etcdlib)
-add_subdirectory(promises)
-add_subdirectory(pushstreams)
+add_library(rcm STATIC
+    src/celix_resource.c
+    src/celix_capability.c
+    src/celix_requirement.c
+    src/celix_rcm_err.c
+)
+set_target_properties(rcm PROPERTIES OUTPUT_NAME "celix_rcm")
+target_include_directories(rcm PRIVATE src)
+target_include_directories(rcm PUBLIC include)
+target_link_libraries(rcm PUBLIC Celix::utils)
 
-add_subdirectory(framework)
+add_library(Celix::rcm ALIAS rcm)
 
-#launcher
-add_subdirectory(launcher)
-
-#add_subdirectory(event_admin)# event_admin is unstable
-add_subdirectory(dependency_manager)
-if (CELIX_CXX14)
-    add_subdirectory(dependency_manager_cxx)
-endif ()
-
-# Error Injectors
-if (ENABLE_TESTING AND LINKER_WRAP_SUPPORTED)
-    add_subdirectory(error_injector)
+if (ENABLE_TESTING)
+    add_subdirectory(gtest)
 endif ()
-
diff --git a/libs/rcm/README.md b/libs/rcm/README.md
new file mode 100644
index 00000000..f77602b6
--- /dev/null
+++ b/libs/rcm/README.md
@@ -0,0 +1,44 @@
+---
+title: RCM library
+---
+
+<!--
+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.
+-->
+
+## Requirement-Capability-Model (RCM) library
+C library based on the OSGi Requirement-Capability-Model, part of 
+[Chapter 3.3](https://docs.osgi.org/specification/osgi.core/8.0.0/framework.module.html#framework.module.dependencies)
+, [Chapter 6](https://docs.osgi.org/specification/osgi.core/8.0.0/framework.resource.html)
+and [Chapter 7](https://docs.osgi.org/specification/osgi.core/8.0.0/framework.wiring.html)
+in the OSGi Core Specification 8.
+
+## Base Requirement-Capability-Model 
+
+The Requirement-Capability-Model (RCM) is a model for describing the capabilities and requirements for a resource.
+This model can be used in bundle wiring to resolve the dependencies between bundles.
+
+The following diagram shows the RCM model:
+
+![Logical RCM Model](diagrams/logical-req-cap-model.png)
+
+## Requirement-Capability-Model and (Bundle) Wiring
+
+TODO
+
+## Requirement-Capability-Model Resolver
+
+TODO
diff --git a/libs/rcm/diagrams/logical-req-cap-model.png b/libs/rcm/diagrams/logical-req-cap-model.png
new file mode 100644
index 00000000..d7a14d69
Binary files /dev/null and b/libs/rcm/diagrams/logical-req-cap-model.png differ
diff --git a/libs/rcm/diagrams/logical-req-cap-model.puml b/libs/rcm/diagrams/logical-req-cap-model.puml
new file mode 100644
index 00000000..e65d3169
--- /dev/null
+++ b/libs/rcm/diagrams/logical-req-cap-model.puml
@@ -0,0 +1,23 @@
+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.
+
+@startuml
+rectangle "Resource"
+rectangle "Capability"
+rectangle "Requirement"
+
+Resource *-up-> "0..n" Requirement
+Resource *-down-> "0..n" Capability
+@enduml
diff --git a/libs/CMakeLists.txt b/libs/rcm/gtest/CMakeLists.txt
similarity index 55%
copy from libs/CMakeLists.txt
copy to libs/rcm/gtest/CMakeLists.txt
index feee84b7..bfe6ef45 100644
--- a/libs/CMakeLists.txt
+++ b/libs/rcm/gtest/CMakeLists.txt
@@ -15,27 +15,21 @@
 # specific language governing permissions and limitations
 # under the License.
 
-#utils, dfi and etcdlib are standalone
-#(e.g. no dependency on celix framework
-add_subdirectory(utils)
-add_subdirectory(dfi)
-add_subdirectory(etcdlib)
-add_subdirectory(promises)
-add_subdirectory(pushstreams)
+set(CMAKE_CXX_STANDARD 17)
 
-add_subdirectory(framework)
+add_executable(test_rcm
+    src/RequirementCapabilityModelTestSuite.cc
+)
 
-#launcher
-add_subdirectory(launcher)
+target_link_libraries(test_rcm PRIVATE Celix::rcm GTest::gtest GTest::gtest_main)
+add_test(NAME test_rcm COMMAND test_rcm)
+setup_target_for_coverage(test_rcm SCAN_DIR ..)
 
-#add_subdirectory(event_admin)# event_admin is unstable
-add_subdirectory(dependency_manager)
-if (CELIX_CXX14)
-    add_subdirectory(dependency_manager_cxx)
-endif ()
-
-# Error Injectors
-if (ENABLE_TESTING AND LINKER_WRAP_SUPPORTED)
-    add_subdirectory(error_injector)
-endif ()
+#TODO
+#add_executable(test_rcm_with_error_injection
+#    src/RequirementCapabilityModelWithErrorInjectionTestSuite.cc
+#)
+#target_link_libraries(test_rcm PRIVATE Celix::rcm GTest::gtest GTest::gtest_main)
+#add_test(NAME test_rcm_with_error_injection COMMAND test_rcm_with_error_injection)
+#setup_target_for_coverage(test_rcm_with_error_injection SCAN_DIR ..)
 
diff --git a/libs/rcm/gtest/src/RequirementCapabilityModelTestSuite.cc b/libs/rcm/gtest/src/RequirementCapabilityModelTestSuite.cc
new file mode 100644
index 00000000..e90b0aa5
--- /dev/null
+++ b/libs/rcm/gtest/src/RequirementCapabilityModelTestSuite.cc
@@ -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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "celix/Properties.h"
+#include "celix_capability.h"
+#include "celix_requirement.h"
+#include "celix_resource.h"
+#include "celix_rcm_err.h"
+
+class RequirementCapabilityModelTestSuite : public ::testing::Test {};
+
+TEST_F(RequirementCapabilityModelTestSuite, TestRequirement) {
+    celix_requirement_t* req = celix_requirement_create(nullptr, "test-namespace", "(&(capability.attribute1=foo)(capability.attribute2=bar))");
+    ASSERT_TRUE(req != nullptr);
+    EXPECT_STREQ("test-namespace", celix_requirement_getNamespace(req));
+    EXPECT_STREQ("(&(capability.attribute1=foo)(capability.attribute2=bar))", celix_requirement_getFilter(req));
+
+    celix_requirement_t* req2 = celix_requirement_create(nullptr, "test-namespace", "(&(capability.attribute1=foo)(capability.attribute2=bar))");
+    ASSERT_TRUE(req2 != nullptr);
+    EXPECT_EQ(celix_requirement_hashCode(req), celix_requirement_hashCode(req2));
+    EXPECT_TRUE(celix_requirement_equals(req, req2));
+
+    celix_requirement_t* req3 = celix_requirement_create(nullptr, "test-namespace2", "(&(capability.attribute1=foo)(capability.attribute2=bar2))"); //note different filter
+    ASSERT_TRUE(req3 != nullptr);
+    EXPECT_NE(celix_requirement_hashCode(req), celix_requirement_hashCode(req3));
+    EXPECT_FALSE(celix_requirement_equals(req, req3));
+    
+    //test attributes/directives
+    EXPECT_EQ(0, celix_properties_size(celix_requirement_getAttributes(req)));
+    EXPECT_EQ(1, celix_properties_size(celix_requirement_getDirectives(req))); //1 filter directive
+    celix_requirement_addAttribute(req, "test-attribute", "test-attribute-value");
+    celix_requirement_addDirective(req, "test-directive", "test-directive-value");
+    EXPECT_EQ(1, celix_properties_size(celix_requirement_getAttributes(req)));
+    EXPECT_EQ(2, celix_properties_size(celix_requirement_getDirectives(req)));
+
+    celix::Properties attr {{"test-attribute1", "test-attribute-value1"}, {"test-attribute2", "test-attribute-value2"}};
+    celix_requirement_addAttributes(req, attr.getCProperties());
+    celix::Properties dir {{"test-directive1", "test-directive-value1"}, {"test-directive2", "test-directive-value2"}};
+    celix_requirement_addDirectives(req, dir.getCProperties());
+    EXPECT_EQ(3, celix_properties_size(celix_requirement_getAttributes(req)));
+    EXPECT_EQ(4, celix_properties_size(celix_requirement_getDirectives(req)));
+
+    //overwrite filter directive
+    celix_requirement_addDirective(req, "filter", "(&(capability.attribute1=foo)(capability.attribute2=bar3))");
+    EXPECT_EQ(4, celix_properties_size(celix_requirement_getDirectives(req)));
+    EXPECT_STREQ("(&(capability.attribute1=foo)(capability.attribute2=bar3))", celix_requirement_getFilter(req));
+
+    EXPECT_FALSE(celix_requirement_equals(req, req2)); //note req 1 changed
+    EXPECT_STREQ(celix_requirement_getDirective(req, "test-directive"), "test-directive-value");
+    EXPECT_EQ(nullptr, celix_requirement_getDirective(req, "test-directive-non-existing"));
+    EXPECT_STREQ(celix_requirement_getAttribute(req, "test-attribute"), "test-attribute-value");
+    EXPECT_EQ(nullptr, celix_requirement_getAttribute(req, "test-attribute-non-existing"));
+
+    celix_requirement_destroy(req);
+    celix_requirement_destroy(req2);
+    celix_requirement_destroy(req3);
+}
+
+TEST_F(RequirementCapabilityModelTestSuite, TestCapability) {
+    celix_capability_t* cap = celix_capability_create(nullptr, "test-namespace");
+    ASSERT_TRUE(cap != nullptr);
+    EXPECT_STREQ("test-namespace", celix_capability_getNamespace(cap));
+
+    celix_capability_t* cap2 = celix_capability_create(nullptr, "test-namespace");
+    ASSERT_TRUE(cap2 != nullptr);
+    EXPECT_EQ(celix_capability_hashCode(cap), celix_capability_hashCode(cap2));
+    EXPECT_TRUE(celix_capability_equals(cap, cap2));
+
+    celix_capability_t* cap3 = celix_capability_create(nullptr, "test-namespace2");
+    ASSERT_TRUE(cap3 != nullptr);
+    EXPECT_NE(celix_capability_hashCode(cap), celix_capability_hashCode(cap3));
+    EXPECT_FALSE(celix_capability_equals(cap, cap3));
+
+    //test attributes/directives
+    EXPECT_EQ(0, celix_properties_size(celix_capability_getAttributes(cap)));
+    EXPECT_EQ(0, celix_properties_size(celix_capability_getDirectives(cap)));
+    celix_capability_addAttribute(cap, "test-attribute", "test-attribute-value");
+    celix_capability_addDirective(cap, "test-directive", "test-directive-value");
+    EXPECT_EQ(1, celix_properties_size(celix_capability_getAttributes(cap)));
+    EXPECT_EQ(1, celix_properties_size(celix_capability_getDirectives(cap)));
+
+    celix::Properties attr {{"test-attribute1", "test-attribute-value1"}, {"test-attribute2", "test-attribute-value2"}};
+    celix_capability_addAttributes(cap, attr.getCProperties());
+    celix::Properties dir {{"test-directive1", "test-directive-value1"}, {"test-directive2", "test-directive-value2"}};
+    celix_capability_addDirectives(cap, attr.getCProperties());
+    EXPECT_EQ(3, celix_properties_size(celix_capability_getAttributes(cap)));
+    EXPECT_EQ(3, celix_properties_size(celix_capability_getDirectives(cap)));
+
+    EXPECT_FALSE(celix_capability_equals(cap, cap2)); //note cap 1 changed
+    EXPECT_STREQ(celix_capability_getDirective(cap, "test-directive"), "test-directive-value");
+    EXPECT_EQ(nullptr, celix_capability_getDirective(cap, "test-directive-non-existing"));
+    EXPECT_STREQ(celix_capability_getAttribute(cap, "test-attribute"), "test-attribute-value");
+    EXPECT_EQ(nullptr, celix_capability_getAttribute(cap, "test-attribute-non-existing"));
+
+    celix_capability_destroy(cap);
+    celix_capability_destroy(cap2);
+    celix_capability_destroy(cap3);
+}
+
+TEST_F(RequirementCapabilityModelTestSuite, TestNoNamespaceForCapabilityAndRequirement) {
+    celix_rcmErr_resetErrors();
+
+    celix_capability_t* cap = celix_capability_create(nullptr, nullptr);
+    EXPECT_TRUE(cap == nullptr);
+    char* err = celix_rcmErr_popLastError();
+    EXPECT_TRUE(err != nullptr);
+    EXPECT_TRUE(strcasestr(err, "namespace") != nullptr) << "Error message should contain 'namespace' but was: " << err;
+    free(err);
+
+    celix_requirement_t* req = celix_requirement_create(nullptr, nullptr, nullptr);
+    EXPECT_TRUE(req == nullptr);
+    err = celix_rcmErr_popLastError();
+    EXPECT_TRUE(err != nullptr);
+    EXPECT_TRUE(strcasestr(err, "namespace") != nullptr) << "Error message should contain 'namespace' but was: " << err;
+    free(err);
+
+    //test reset
+    cap = celix_capability_create(nullptr, nullptr);
+    EXPECT_TRUE(cap == nullptr);
+    req = celix_requirement_create(nullptr, nullptr, nullptr);
+    EXPECT_TRUE(req == nullptr);
+    EXPECT_EQ(2, celix_rcmErr_getErrorCount());
+    celix_rcmErr_resetErrors(); //should clear 2 error messages
+    EXPECT_EQ(0, celix_rcmErr_getErrorCount());
+}
+
+TEST_F(RequirementCapabilityModelTestSuite, TestResource) {
+    celix_resource_t* res = celix_resource_create();
+    ASSERT_TRUE(res != nullptr);
+    EXPECT_EQ(0, celix_arrayList_size(celix_resource_getRequirements(res, nullptr)));
+    EXPECT_EQ(0, celix_arrayList_size(celix_resource_getCapabilities(res, nullptr)));
+
+    celix_resource_t* res2 = celix_resource_create();
+    ASSERT_TRUE(res2 != nullptr);
+    EXPECT_EQ(0, celix_arrayList_size(celix_resource_getRequirements(res, nullptr)));
+    EXPECT_EQ(0, celix_arrayList_size(celix_resource_getCapabilities(res, nullptr)));
+
+    EXPECT_EQ(celix_resource_hashCode(res), celix_resource_hashCode(res2));
+    EXPECT_TRUE(celix_resource_equals(res, res2));
+
+    celix_requirement_t* req = celix_requirement_create(res, "test-namespace", "(&(capability.attribute1=foo)(capability.attribute2=bar))");
+    celix_resource_addRequirement(res, req);
+    EXPECT_EQ(res, celix_requirement_getResource(req));
+    EXPECT_EQ(1, celix_arrayList_size(celix_resource_getRequirements(res, nullptr)));
+
+    celix_capability_t *cap = celix_capability_create(res, "test-namespace");
+    celix_resource_addCapability(res, cap);
+    EXPECT_EQ(res, celix_capability_getResource(cap));
+    EXPECT_EQ(1, celix_arrayList_size(celix_resource_getCapabilities(res, nullptr)));
+
+    EXPECT_FALSE(celix_resource_equals(res, res2)); //note res 1 changed
+    EXPECT_NE(celix_resource_hashCode(res), celix_resource_hashCode(res2));
+
+    req = celix_requirement_create(res2, "test-namespace", "(&(capability.attribute1=foo)(capability.attribute2=bar))");
+    cap = celix_capability_create(res2, "test-namespace");
+    EXPECT_TRUE(celix_resource_addRequirement(res2, req));
+    EXPECT_TRUE(celix_resource_addCapability(res2, cap));
+    EXPECT_TRUE(celix_resource_equals(res, res2)); //note res and res2 are the same again
+    EXPECT_EQ(celix_resource_hashCode(res), celix_resource_hashCode(res2));
+
+    //test get capabilities/requirements by namespace
+    req = celix_requirement_create(res, "test-namespace2", nullptr);
+    cap = celix_capability_create(res, "test-namespace2");
+    EXPECT_TRUE(celix_resource_addRequirement(res, req));
+    EXPECT_TRUE(celix_resource_addCapability(res, cap));
+    EXPECT_EQ(2, celix_arrayList_size(celix_resource_getRequirements(res, nullptr)));
+    EXPECT_EQ(2, celix_arrayList_size(celix_resource_getCapabilities(res, nullptr)));
+    const celix_array_list_t* caps = celix_resource_getCapabilities(res, "test-namespace");
+    EXPECT_EQ(1, celix_arrayList_size(caps));
+    const celix_array_list_t* reqs = celix_resource_getRequirements(res, "test-namespace");
+    EXPECT_EQ(1, celix_arrayList_size(reqs));
+
+    celix_resource_destroy(res);
+    celix_resource_destroy(res2);
+}
+
+TEST_F(RequirementCapabilityModelTestSuite, TestCapabilityAndRequirementWithWrongResource) {
+    celix_resource_t* res = celix_resource_create();
+    ASSERT_TRUE(res != nullptr);
+
+    celix_capability_t* cap = celix_capability_create(nullptr, "test-namespace");
+    celix_requirement_t* req = celix_requirement_create(nullptr, "test-namespace", nullptr);
+
+    celix_rcmErr_resetErrors();
+    EXPECT_FALSE(celix_resource_addCapability(res, cap));
+    EXPECT_FALSE(celix_resource_addRequirement(res, req));
+    EXPECT_EQ(2, celix_rcmErr_getErrorCount());
+    celix_requirement_destroy(req);
+    celix_capability_destroy(cap);
+
+    char* err = celix_rcmErr_popLastError();
+    EXPECT_TRUE(strcasestr(err, "capability") != nullptr) << "Error message should contain 'capability' but was: " << err;
+    free(err);
+    err = celix_rcmErr_popLastError();
+    EXPECT_TRUE(strcasestr(err, "requirement") != nullptr) << "Error message should contain 'requirement' but was: " << err;
+    free(err);
+
+    celix_resource_destroy(res);
+}
diff --git a/libs/rcm/include/celix_capability.h b/libs/rcm/include/celix_capability.h
new file mode 100644
index 00000000..06ec7b41
--- /dev/null
+++ b/libs/rcm/include/celix_capability.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef CELIX_CELIX_CAPABILITY_H
+#define CELIX_CELIX_CAPABILITY_H
+
+#include <stdbool.h>
+
+#include "celix_rcm_types.h"
+#include "celix_properties.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+* @file celix_capability.h
+* @brief The celix_capability_t is a struct which represents a capability.
+*
+* @thread_safety none
+*/
+
+celix_capability_t* celix_capability_create(
+        const celix_resource_t* resource,
+        const char* ns);
+
+void celix_capability_destroy(celix_capability_t* capability);
+
+bool celix_capability_equals(const celix_capability_t* cap1, const celix_capability_t* cap2);
+
+unsigned int celix_capability_hashCode(const celix_capability_t* cap);
+
+const celix_resource_t* celix_capability_getResource(const celix_capability_t* cap);
+
+const char* celix_capability_getNamespace(const celix_capability_t* cap);
+
+const celix_properties_t* celix_capability_getAttributes(const celix_capability_t* cap);
+
+const celix_properties_t* celix_capability_getDirectives(const celix_capability_t* cap);
+
+const char* celix_capability_getAttribute(const celix_capability_t* cap, const char* key);
+
+const char* celix_capability_getDirective(const celix_capability_t* cap, const char* key);
+
+void celix_capability_addAttribute(celix_capability_t* cap, const char* key, const char* value);
+
+void celix_capability_addAttributes(celix_capability_t* cap, const celix_properties_t* attributes);
+
+void celix_capability_addDirective(celix_capability_t* cap, const char* key, const char* value);
+
+void celix_capability_addDirectives(celix_capability_t* cap, const celix_properties_t* directives);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //CELIX_CELIX_CAPABILITY_H
diff --git a/libs/rcm/include/celix_rcm_err.h b/libs/rcm/include/celix_rcm_err.h
new file mode 100644
index 00000000..4ee07e78
--- /dev/null
+++ b/libs/rcm/include/celix_rcm_err.h
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef CELIX_CELIX_RCM_ERR_H
+#define CELIX_CELIX_RCM_ERR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file celix_rcm_err.h
+ * @brief The rcm error handling functions.
+ * @thread_safety Thread safe.
+ */
+
+/**
+ * @brief Returns the last error message from the current thread.
+ * The error message must be freed by the caller.
+ * @returnval NULL if no error message is available.
+ */
+char* celix_rcmErr_popLastError();
+
+//TBD, maybe remove a error struct instead of a single error message, something like:
+//typedef struct celix_rcm_error {
+//    celix_status_t status;
+//    char* msg;
+//} celix_rcm_error_t;
+// celix_rcm_error_t* celix_rcmErr_popLastError();
+// celix_rcmErr_destroyError(celix_rcm_error_t* err);
+
+/**
+ * @brief Returns the number of error messages from the current thread.
+ */
+int celix_rcmErr_getErrorCount();
+
+/**
+ * @brief Resets the error message for the current thread.
+ */
+void celix_rcmErr_resetErrors();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //CELIX_CELIX_RCM_ERR_H
diff --git a/libs/rcm/include/celix_rcm_types.h b/libs/rcm/include/celix_rcm_types.h
new file mode 100644
index 00000000..278e1c10
--- /dev/null
+++ b/libs/rcm/include/celix_rcm_types.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef CELIX_CELIX_RCM_TYPES_H
+#define CELIX_CELIX_RCM_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+* @file celix_rcm_types.h
+* @brief The celix_rcm_types.h header file contains the forward declarations of the celix_resource_t, celix_capability_t and celix_requirement_t types.
+*/
+
+//forward declarations of opaque types
+typedef struct celix_resource celix_resource_t;
+typedef struct celix_capability celix_capability_t;
+typedef struct celix_requirement celix_requirement_t;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //CELIX_CELIX_RCM_TYPES_H
diff --git a/libs/rcm/include/celix_requirement.h b/libs/rcm/include/celix_requirement.h
new file mode 100644
index 00000000..6c463bbf
--- /dev/null
+++ b/libs/rcm/include/celix_requirement.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef CELIX_CELIX_REQUIREMENT_H
+#define CELIX_CELIX_REQUIREMENT_H
+
+#include <stdbool.h>
+
+#include "celix_rcm_types.h"
+#include "celix_properties.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+* @file celix_requirement.h
+* @brief The celix_requirement_t is a requirement for a capability.
+*
+* A requirement is a requirement for a capability. A requirement has a namespace, a filter and a set of directives and attributes.
+* The namespace is used to identify the type of requirement. The filter is used to filter the capabilities.
+* The directives and attributes are used to configure the requirement.
+*
+* @thread_safety none
+*/
+
+celix_requirement_t* celix_requirement_create(
+        celix_resource_t* resource,
+        const char* ns,
+        const char* filter);
+
+
+void celix_requirement_destroy(celix_requirement_t* requirement);
+
+bool celix_requirement_equals(const celix_requirement_t* req1, const celix_requirement_t* req2);
+
+unsigned int celix_requirement_hashCode(const celix_requirement_t* req);
+
+const celix_resource_t* celix_requirement_getResource(const celix_requirement_t* req);
+
+const char* celix_requirement_getNamespace(const celix_requirement_t* req);
+
+const char* celix_requirement_getFilter(const celix_requirement_t* req);
+
+const celix_properties_t* celix_requirement_getDirectives(const celix_requirement_t* req);
+
+const char* celix_requirement_getDirective(const celix_requirement_t* req, const char* key);
+
+const celix_properties_t* celix_requirement_getAttributes(const celix_requirement_t* req);
+
+const char* celix_requirement_getAttribute(const celix_requirement_t* req, const char* key);
+
+void celix_requirement_addDirective(celix_requirement_t* req, const char* key, const char* value);
+
+void celix_requirement_addDirectives(celix_requirement_t* req, const celix_properties_t* directives);
+
+void celix_requirement_addAttribute(celix_requirement_t* req, const char* key, const char* value);
+
+void celix_requirement_addAttributes(celix_requirement_t* req, const celix_properties_t* attributes);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //CELIX_CELIX_REQUIREMENT_H
diff --git a/libs/rcm/include/celix_resource.h b/libs/rcm/include/celix_resource.h
new file mode 100644
index 00000000..59b31cf1
--- /dev/null
+++ b/libs/rcm/include/celix_resource.h
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef CELIX_CELIX_RESOURCE_H
+#define CELIX_CELIX_RESOURCE_H
+
+#include <stdbool.h>
+
+#include "celix_rcm_types.h"
+#include "celix_array_list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+* @file celix_resource.h
+* @brief The celix_resource_t is a resource in the RCM.
+*
+* A resource is a collection of capabilities and requirements.
+*
+* @thread_safety none
+*/
+
+celix_resource_t* celix_resource_create();
+
+void celix_resource_destroy(celix_resource_t* resource);
+
+bool celix_resource_equals(const celix_resource_t* res1, const celix_resource_t* res2);
+
+unsigned int celix_resource_hashCode(const celix_resource_t* res);
+
+const celix_array_list_t* celix_resource_getCapabilities(const celix_resource_t* res, const char* ns);
+
+const celix_array_list_t* celix_resource_getRequirements(const celix_resource_t* res, const char* ns);
+
+bool celix_resource_addCapability(celix_resource_t* res, celix_capability_t* cap);
+
+bool celix_resource_addRequirement(celix_resource_t* res, celix_requirement_t* req);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //CELIX_CELIX_RESOURCE_H
diff --git a/libs/rcm/src/celix_capability.c b/libs/rcm/src/celix_capability.c
new file mode 100644
index 00000000..7f11f95c
--- /dev/null
+++ b/libs/rcm/src/celix_capability.c
@@ -0,0 +1,171 @@
+/*
+ * 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 <stdlib.h>
+
+#include "celix_utils.h"
+#include "celix_capability.h"
+#include "celix_rcm_err_private.h"
+
+struct celix_capability {
+    const celix_resource_t* resource; //weak ref
+    char* ns;
+    celix_properties_t* attributes;
+    celix_properties_t* directives;
+};
+
+celix_capability_t* celix_capability_create(
+        const celix_resource_t* resource,
+        const char* ns) {
+
+    if (celix_utils_isStringNullOrEmpty(ns)) {
+        celix_rcmErr_push("Failed to create celix_capability_t. Namespace is NULL or empty.");
+        return NULL;
+    }
+
+    celix_capability_t* cap = malloc(sizeof(*cap));
+    if (cap != NULL) {
+        cap->resource = resource;
+        cap->ns = celix_utils_strdup(ns);
+        cap->attributes = celix_properties_create();
+        cap->directives = celix_properties_create();
+        if (cap->ns == NULL || cap->attributes == NULL || cap->directives == NULL) {
+            celix_rcmErr_push("Failed to allocate celix_capability_t fields. Out of memory.");
+            celix_capability_destroy(cap);
+            cap = NULL;
+        }
+    } else {
+        celix_rcmErr_push("Failed to allocate celix_capability_t. Out of memory.");
+    }
+    return cap;
+}
+
+void celix_capability_destroy(celix_capability_t* capability) {
+    if (capability != NULL) {
+        free(capability->ns);
+        celix_properties_destroy(capability->attributes);
+        celix_properties_destroy(capability->directives);
+        free(capability);
+    }
+}
+
+bool celix_capability_equals(const celix_capability_t* cap1, const celix_capability_t* cap2) {
+    if (cap1 == cap2) {
+        return true;
+    }
+
+    if (celix_properties_size(cap1->attributes) != celix_properties_size(cap2->attributes) ||
+        celix_properties_size(cap1->directives) != celix_properties_size(cap2->directives)) {
+        return false;
+    }
+
+    if (!celix_utils_stringEquals(cap1->ns, cap2->ns)) {
+        return false;
+    }
+
+    //compare attributes
+    bool equals = true;
+    const char* visit;
+    CELIX_PROPERTIES_FOR_EACH(cap1->attributes, visit) {
+        const char* value1 = celix_properties_get(cap1->attributes, visit, NULL);
+        const char* value2 = celix_properties_get(cap2->attributes, visit, NULL);
+        if (!celix_utils_stringEquals(value1, value2)) {
+            equals = false;
+            break;
+        }
+    }
+    if (!equals) {
+        return false;
+    }
+    CELIX_PROPERTIES_FOR_EACH(cap1->directives, visit) {
+        const char* value1 = celix_properties_get(cap1->directives, visit, NULL);
+        const char* value2 = celix_properties_get(cap2->directives, visit, NULL);
+        if (!celix_utils_stringEquals(value1, value2)) {
+            equals = false;
+            break;
+        }
+    }
+    return equals;
+}
+
+unsigned int celix_capability_hashCode(const celix_capability_t* cap) {
+    unsigned int hash = celix_utils_stringHash(cap->ns);
+    const char* visit;
+
+    //FIXME order of attributes/directives is not taken into account
+    CELIX_PROPERTIES_FOR_EACH(cap->attributes, visit) {
+        const char* value = celix_properties_get(cap->attributes, visit, NULL);
+        hash += celix_utils_stringHash(visit);
+        hash += celix_utils_stringHash(value);
+    }
+    CELIX_PROPERTIES_FOR_EACH(cap->directives, visit) {
+        const char* value = celix_properties_get(cap->directives, visit, NULL);
+        hash += celix_utils_stringHash(visit);
+        hash += celix_utils_stringHash(value);
+    }
+    return hash;
+}
+
+const celix_resource_t* celix_capability_getResource(const celix_capability_t* cap) {
+    return cap->resource;
+}
+
+const char* celix_capability_getNamespace(const celix_capability_t* cap) {
+    return cap->ns;
+}
+
+const celix_properties_t* celix_capability_getAttributes(const celix_capability_t* cap) {
+    return cap->attributes;
+}
+
+const celix_properties_t* celix_capability_getDirectives(const celix_capability_t* cap) {
+    return cap->directives;
+}
+
+const char* celix_capability_getAttribute(const celix_capability_t* cap, const char* key) {
+    return celix_properties_get(cap->attributes, key, NULL);
+}
+
+const char* celix_capability_getDirective(const celix_capability_t* cap, const char* key) {
+    return celix_properties_get(cap->directives, key, NULL);
+}
+
+void celix_capability_addAttribute(celix_capability_t* cap, const char* key, const char* value) {
+    celix_properties_set(cap->attributes, key, value);
+}
+
+void celix_capability_addAttributes(celix_capability_t* cap, const celix_properties_t* attributes) {
+    const char* visit;
+    CELIX_PROPERTIES_FOR_EACH(attributes, visit) {
+        const char* value = celix_properties_get(attributes, visit, NULL);
+        celix_properties_set(cap->attributes, visit, value);
+    }
+}
+
+void celix_capability_addDirective(celix_capability_t* cap, const char* key, const char* value) {
+    celix_properties_set(cap->directives, key, value);
+}
+
+void celix_capability_addDirectives(celix_capability_t* cap, const celix_properties_t* directives) {
+    const char* visit;
+    CELIX_PROPERTIES_FOR_EACH(directives, visit) {
+        const char* value = celix_properties_get(directives, visit, NULL);
+        celix_properties_set(cap->directives, visit, value);
+    }
+}
diff --git a/libs/rcm/src/celix_rcm_err.c b/libs/rcm/src/celix_rcm_err.c
new file mode 100644
index 00000000..d98fdbbf
--- /dev/null
+++ b/libs/rcm/src/celix_rcm_err.c
@@ -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.
+ */
+
+#include <stdlib.h>
+#include <threads.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "celix_array_list.h"
+#include "celix_rcm_err_private.h"
+#include "celix_rcm_err.h"
+#include "celix_utils.h"
+
+static tss_t celix_rcm_tssErrorsKey;
+
+static void celix_rcm_destroyTssErrors(void* data) {
+    celix_array_list_t* errors = data;
+    if (errors != NULL) {
+        for (int i = 0; i < celix_arrayList_size(errors); ++i) {
+            char* msg = celix_arrayList_get(errors, i);
+            free(msg);
+        }
+    }
+    celix_arrayList_destroy(errors);
+}
+
+__attribute__((constructor)) static void celix_rcm_initThreadSpecificStorageKey() {
+    tss_create(&celix_rcm_tssErrorsKey, celix_rcm_destroyTssErrors);
+}
+
+__attribute__((destructor)) static void celix_rcm_deinitThreadSpecificStorageKey() {
+    tss_delete(celix_rcm_tssErrorsKey);
+}
+
+char* celix_rcmErr_popLastError() {
+    char* result = NULL;
+    celix_array_list_t* errors = tss_get(celix_rcm_tssErrorsKey);
+    if (errors != NULL && celix_arrayList_size(errors) > 0) {
+        result = celix_arrayList_get(errors, 0);
+        celix_arrayList_removeAt(errors, 0);
+    }
+    return result;
+}
+
+int celix_rcmErr_getErrorCount() {
+    int result = 0;
+    celix_array_list_t* errors = tss_get(celix_rcm_tssErrorsKey);
+    if (errors != NULL) {
+        result = celix_arrayList_size(errors);
+    }
+    return result;
+}
+
+void celix_rcmErr_resetErrors() {
+    celix_array_list_t* errors = tss_get(celix_rcm_tssErrorsKey);
+    if (errors != NULL) {
+        for (int i = 0; i < celix_arrayList_size(errors); ++i) {
+            char* msg = celix_arrayList_get(errors, i);
+            free(msg);
+        }
+        celix_arrayList_clear(errors);
+    }
+}
+
+static void celix_rcm_pushMsg(char* msg) {
+    celix_array_list_t* errors = tss_get(celix_rcm_tssErrorsKey);
+    if (errors == NULL) {
+        errors = celix_arrayList_create();
+        tss_set(celix_rcm_tssErrorsKey, errors);
+    }
+    if (errors != NULL) {
+        celix_arrayList_add(errors, msg);
+    }
+}
+
+void celix_rcmErr_push(const char* msg) {
+    celix_rcm_pushMsg(celix_utils_strdup(msg));
+}
+
+void celix_rcmErr_pushf(char* format, ...) {
+    va_list args;
+    va_start(args, format);
+    char* msg = NULL;
+    int rc = vasprintf(&msg, format, args);
+    if (rc >= 0) {
+        celix_rcm_pushMsg(msg);
+    }
+    va_end(args);
+}
diff --git a/libs/rcm/src/celix_rcm_err_private.h b/libs/rcm/src/celix_rcm_err_private.h
new file mode 100644
index 00000000..83b353be
--- /dev/null
+++ b/libs/rcm/src/celix_rcm_err_private.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#ifndef CELIX_CELIX_RCM_ERR_PRIVATE_H
+#define CELIX_CELIX_RCM_ERR_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Push an formatted error message to the thread specific storage rcm errors.
+ */
+void celix_rcmErr_pushf(char* format, ...) __attribute__((format(printf, 1, 2)));
+
+/**
+ * @brief Push an error message to the thread specific storage rcm errors.
+ */
+void celix_rcmErr_push(const char* msg);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //CELIX_CELIX_RCM_ERR_PRIVATE_H
diff --git a/libs/rcm/src/celix_requirement.c b/libs/rcm/src/celix_requirement.c
new file mode 100644
index 00000000..38b940cc
--- /dev/null
+++ b/libs/rcm/src/celix_requirement.c
@@ -0,0 +1,182 @@
+/*
+ * 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 <stdlib.h>
+
+#include "celix_utils.h"
+#include "celix_requirement.h"
+#include "celix_rcm_err_private.h"
+
+#define CELIX_REQUIREMENT_DIRECTIVE_FILTER "filter"
+
+struct celix_requirement {
+    const celix_resource_t* resource; //weak ref
+    char* ns;
+    celix_properties_t* attributes;
+    celix_properties_t* directives;
+};
+
+celix_requirement_t* celix_requirement_create(
+        celix_resource_t* resource,
+        const char* ns,
+        const char* filter) {
+
+    if (celix_utils_isStringNullOrEmpty(ns)) {
+        celix_rcmErr_push("Failed to create celix_requirement_t. Namespace is NULL or empty.");
+        return NULL;
+    }
+
+    celix_requirement_t* req = malloc(sizeof(*req));
+    if (req != NULL) {
+        req->resource = resource;
+        req->ns = celix_utils_strdup(ns);
+        req->attributes = celix_properties_create();
+        req->directives = celix_properties_create();
+        if (req->ns == NULL || req->attributes == NULL || req->directives == NULL) {
+            celix_rcmErr_push("Failed to allocate celix_requirement_t fields. Out of memory.");
+            celix_requirement_destroy(req);
+            req = NULL;
+        } else if (filter != NULL) {
+            celix_properties_set(req->directives, CELIX_REQUIREMENT_DIRECTIVE_FILTER, filter);
+        }
+    } else {
+        celix_rcmErr_push("Failed to allocate celix_requirement_t. Out of memory.");
+    }
+    return req;
+}
+
+void celix_requirement_destroy(celix_requirement_t* requirement) {
+    if (requirement != NULL) {
+        free(requirement->ns);
+        celix_properties_destroy(requirement->attributes);
+        celix_properties_destroy(requirement->directives);
+        free(requirement);
+    }
+}
+
+bool celix_requirement_equals(const celix_requirement_t* req1, const celix_requirement_t* req2) {
+    if (req1 == req2) {
+        return true;
+    }
+
+    if (celix_properties_size(req1->attributes) != celix_properties_size(req2->attributes) ||
+        celix_properties_size(req1->directives) != celix_properties_size(req2->directives)) {
+        return false;
+    }
+
+    if (!celix_utils_stringEquals(req1->ns, req2->ns)) {
+        return false;
+    }
+
+    //compare attributes
+    bool equals = true;
+    const char* visit;
+    CELIX_PROPERTIES_FOR_EACH(req1->attributes, visit) {
+        const char* val1 = celix_properties_get(req1->attributes, visit, NULL);
+        const char* val2 = celix_properties_get(req2->attributes, visit, NULL);
+        if (!celix_utils_stringEquals(val1, val2)) {
+            equals = false;
+            break;
+        }
+    }
+    if (!equals) {
+        return false;
+    }
+
+    //compare directives
+    CELIX_PROPERTIES_FOR_EACH(req1->directives, visit) {
+        const char* val1 = celix_properties_get(req1->directives, visit, NULL);
+        const char* val2 = celix_properties_get(req2->directives, visit, NULL);
+        if (!celix_utils_stringEquals(val1, val2)) {
+            equals = false;
+            break;
+        }
+    }
+    return equals;
+}
+
+unsigned int celix_requirement_hashCode(const celix_requirement_t* req) {
+    unsigned int hash = celix_utils_stringHash(req->ns);
+    const char* visit;
+
+    //FIXME order of attributes/directives is not taken into account
+    CELIX_PROPERTIES_FOR_EACH(req->attributes, visit) {
+        const char* val = celix_properties_get(req->attributes, visit, NULL);
+        hash += celix_utils_stringHash(visit);
+        hash += celix_utils_stringHash(val);
+    }
+    CELIX_PROPERTIES_FOR_EACH(req->directives, visit) {
+        const char* val = celix_properties_get(req->directives, visit, NULL);
+        hash += celix_utils_stringHash(visit);
+        hash += celix_utils_stringHash(val);
+    }
+    return hash;
+}
+
+const celix_resource_t* celix_requirement_getResource(const celix_requirement_t* req) {
+    return req->resource;
+}
+
+const char* celix_requirement_getNamespace(const celix_requirement_t* req) {
+    return req->ns;
+}
+
+const char* celix_requirement_getFilter(const celix_requirement_t* req) {
+    return celix_properties_get(req->directives, CELIX_REQUIREMENT_DIRECTIVE_FILTER, NULL);
+}
+
+const celix_properties_t* celix_requirement_getDirectives(const celix_requirement_t* req) {
+    return req->directives;
+}
+
+const char* celix_requirement_getDirective(const celix_requirement_t* req, const char* key) {
+    return celix_properties_get(req->directives, key, NULL);
+}
+
+const celix_properties_t* celix_requirement_getAttributes(const celix_requirement_t* req) {
+    return req->attributes;
+}
+
+const char* celix_requirement_getAttribute(const celix_requirement_t* req, const char* key) {
+    return celix_properties_get(req->attributes, key, NULL);
+}
+
+void celix_requirement_addDirective(celix_requirement_t* req, const char* key, const char* value) {
+    celix_properties_set(req->directives, key, value);
+}
+
+void celix_requirement_addAttribute(celix_requirement_t* req, const char* key, const char* value) {
+    celix_properties_set(req->attributes, key, value);
+}
+
+void celix_requirement_addDirectives(celix_requirement_t* req, const celix_properties_t* directives) {
+    const char* visit;
+    CELIX_PROPERTIES_FOR_EACH(directives, visit) {
+        const char* val = celix_properties_get(directives, visit, NULL);
+        celix_requirement_addDirective(req, visit, val);
+    }
+}
+
+void celix_requirement_addAttributes(celix_requirement_t* req, const celix_properties_t* attributes) {
+    const char* visit;
+    CELIX_PROPERTIES_FOR_EACH(attributes, visit) {
+        const char* val = celix_properties_get(attributes, visit, NULL);
+        celix_requirement_addAttribute(req, visit, val);
+    }
+}
diff --git a/libs/rcm/src/celix_resource.c b/libs/rcm/src/celix_resource.c
new file mode 100644
index 00000000..6b41f457
--- /dev/null
+++ b/libs/rcm/src/celix_resource.c
@@ -0,0 +1,183 @@
+/*
+ * 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 <stdlib.h>
+
+#include "celix_string_hash_map.h"
+#include "celix_rcm_err_private.h"
+#include "celix_resource.h"
+#include "celix_capability.h"
+#include "celix_requirement.h"
+
+struct celix_resource {
+    celix_array_list_t* allCapabilities;
+    celix_array_list_t* allRequirements;
+    celix_string_hash_map_t* capabilitiesByNamespace; //note weak ref to capabilities in allCapabilities
+    celix_string_hash_map_t* requirementsByNamespace; //note weak ref to requirements in allRequirements
+};
+
+static void celix_resource_destroyCapability(void* capability) {
+    celix_capability_destroy((celix_capability_t*)capability);
+}
+
+static void celix_resource_destroyRequirement(void* requirement) {
+    celix_requirement_destroy((celix_requirement_t*)requirement);
+}
+
+static void celix_resource_freeReqOrCapList(void* list) {
+    celix_arrayList_destroy((celix_array_list_t*)list);
+}
+
+celix_resource_t* celix_resource_create() {
+    celix_resource_t* res = malloc(sizeof(*res));
+    if (res != NULL) {
+        celix_array_list_create_options_t opts = CELIX_EMPTY_ARRAY_LIST_CREATE_OPTIONS;
+        opts.simpleRemovedCallback = celix_resource_destroyCapability;
+        res->allCapabilities = celix_arrayList_createWithOptions(&opts);
+
+        opts.simpleRemovedCallback = celix_resource_destroyRequirement;
+        res->allRequirements = celix_arrayList_createWithOptions(&opts);
+
+        celix_string_hash_map_create_options_t mapOpts = CELIX_EMPTY_STRING_HASH_MAP_CREATE_OPTIONS;
+        mapOpts.simpleRemovedCallback = celix_resource_freeReqOrCapList;
+        res->capabilitiesByNamespace = celix_stringHashMap_createWithOptions(&mapOpts);
+        res->requirementsByNamespace = celix_stringHashMap_createWithOptions(&mapOpts);
+
+        if (    res->allCapabilities == NULL ||
+                res->allRequirements == NULL ||
+                res->capabilitiesByNamespace == NULL ||
+                res->requirementsByNamespace == NULL) {
+            celix_rcmErr_push("Failed to allocate celix_resource_t fields. Out of memory.");
+            celix_resource_destroy(res);
+            res = NULL;
+        }
+    } else {
+        celix_rcmErr_push("Failed to allocate celix_resource_t. Out of memory.");
+    }
+    return res;
+}
+
+void celix_resource_destroy(celix_resource_t* resource) {
+    if (resource != NULL) {
+        celix_arrayList_destroy(resource->allCapabilities);
+        celix_arrayList_destroy(resource->allRequirements);
+        celix_stringHashMap_destroy(resource->capabilitiesByNamespace);
+        celix_stringHashMap_destroy(resource->requirementsByNamespace);
+        free(resource);
+    }
+}
+
+bool celix_resource_equals(const celix_resource_t* res1, const celix_resource_t* res2) {
+    if (res1 == res2) {
+        return true;
+    }
+
+    if (celix_arrayList_size(res1->allCapabilities) != celix_arrayList_size(res2->allCapabilities) ||
+        celix_arrayList_size(res1->allRequirements) != celix_arrayList_size(res2->allRequirements)) {
+        return false;
+    }
+
+    //FIXME this is not correct, as the order of the capabilities and requirements is not important
+    for (int i = 0; i < celix_arrayList_size(res1->allCapabilities); ++i) {
+        celix_capability_t* cap1 = celix_arrayList_get(res1->allCapabilities, i);
+        celix_capability_t* cap2 = celix_arrayList_get(res2->allCapabilities, i);
+        if (!celix_capability_equals(cap1, cap2)) {
+            return false;
+        }
+    }
+
+    //FIXME this is not correct, as the order of the capabilities and requirements is not important
+    for (int i = 0; i < celix_arrayList_size(res1->allCapabilities); ++i) {
+        celix_requirement_t* req1 = celix_arrayList_get(res1->allCapabilities, i);
+        celix_requirement_t* req2 = celix_arrayList_get(res2->allCapabilities, i);
+        if (!celix_requirement_equals(req1, req2)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+unsigned int celix_resource_hashCode(const celix_resource_t* res) {
+    unsigned int hash = 0;
+
+    //FIXME this is not correct, as the order of the capabilities and requirements is not important
+    for (int i = 0; i < celix_arrayList_size(res->allCapabilities); ++i) {
+        celix_capability_t* cap = celix_arrayList_get(res->allCapabilities, i);
+        hash += celix_capability_hashCode(cap);
+    }
+
+    //FIXME this is not correct, as the order of the capabilities and requirements is not important
+    for (int i = 0; i < celix_arrayList_size(res->allRequirements); ++i) {
+        celix_requirement_t* req = celix_arrayList_get(res->allRequirements, i);
+        hash += celix_requirement_hashCode(req);
+    }
+    return hash;
+}
+
+const celix_array_list_t* celix_resource_getCapabilities(const celix_resource_t* res, const char* ns) {
+    if (ns != NULL) {
+        return celix_stringHashMap_get(res->capabilitiesByNamespace, ns);
+    }
+    return res->allCapabilities;
+}
+
+const celix_array_list_t* celix_resource_getRequirements(const celix_resource_t* res, const char* ns) {
+    if (ns != NULL) {
+        return celix_stringHashMap_get(res->requirementsByNamespace, ns);
+    }
+    return res->allRequirements;
+}
+
+bool celix_resource_addCapability(celix_resource_t* res, celix_capability_t* cap) {
+    if (celix_capability_getResource(cap) != res) {
+        celix_rcmErr_pushf(
+                "Capability is not added to the correct resource. (Capability is added to resource %p, but resource %p is expected.)",
+                celix_capability_getResource(cap), res);
+        return false;
+    }
+
+    celix_arrayList_add(res->allCapabilities, cap);
+    const char* ns = celix_capability_getNamespace(cap);
+    celix_array_list_t* caps = celix_stringHashMap_get(res->capabilitiesByNamespace, ns);
+    if (caps == NULL) {
+        caps = celix_arrayList_create();
+        celix_stringHashMap_put(res->capabilitiesByNamespace, ns, caps);
+    }
+    celix_arrayList_add(caps, cap);
+    return true;
+}
+
+bool celix_resource_addRequirement(celix_resource_t* res, celix_requirement_t* req) {
+    if (celix_requirement_getResource(req) != res) {
+        celix_rcmErr_pushf(
+                "Requirement is not added to the correct resource. (Requirement is added to resource %p, but resource %p is expected.)",
+                celix_requirement_getResource(req), res);
+        return false;
+    }
+
+    celix_arrayList_add(res->allRequirements, req);
+    const char* ns = celix_requirement_getNamespace(req);
+    celix_array_list_t* reqs = celix_stringHashMap_get(res->requirementsByNamespace, ns);
+    if (reqs == NULL) {
+        reqs = celix_arrayList_create();
+        celix_stringHashMap_put(res->requirementsByNamespace, ns, reqs);
+    }
+    celix_arrayList_add(reqs, req);
+    return true;
+}