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

pnoltes pushed a commit to branch feature/551-add-1st-part-req-cap-model-lib
in repository https://gitbox.apache.org/repos/asf/celix.git

commit 6268ea704a66082f450970afcda5b95580fe2851
Author: Pepijn Noltes <[email protected]>
AuthorDate: Sun May 7 14:09:10 2023 +0200

    #551: Add 1st part of the requirement-capability-model lib
---
 libs/CMakeLists.txt                                |   1 +
 libs/{ => rcm}/CMakeLists.txt                      |  41 ++--
 libs/rcm/README.md                                 |  51 ++++
 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                      |  38 +++
 .../src/RequirementCapabilityModelTestSuite.cc     | 263 +++++++++++++++++++++
 ...ntCapabilityModelWithErrorInjectionTestSuite.cc | 133 +++++++++++
 libs/rcm/include/celix_capability.h                | 161 +++++++++++++
 libs/rcm/include/celix_rcm_types.h                 |  42 ++++
 libs/rcm/include/celix_requirement.h               | 173 ++++++++++++++
 libs/rcm/include/celix_resource.h                  | 112 +++++++++
 libs/rcm/src/celix_capability.c                    | 172 ++++++++++++++
 libs/rcm/src/celix_requirement.c                   | 185 +++++++++++++++
 libs/rcm/src/celix_resource.c                      | 155 ++++++++++++
 15 files changed, 1528 insertions(+), 22 deletions(-)

diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt
index feee84b7..cb8481a2 100644
--- a/libs/CMakeLists.txt
+++ b/libs/CMakeLists.txt
@@ -18,6 +18,7 @@
 #utils, dfi and etcdlib are standalone
 #(e.g. no dependency on celix framework
 add_subdirectory(utils)
+add_subdirectory(rcm)
 add_subdirectory(dfi)
 add_subdirectory(etcdlib)
 add_subdirectory(promises)
diff --git a/libs/CMakeLists.txt b/libs/rcm/CMakeLists.txt
similarity index 51%
copy from libs/CMakeLists.txt
copy to libs/rcm/CMakeLists.txt
index feee84b7..23e42266 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,24 @@
 # 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(RCM_SOURCES src/celix_resource.c src/celix_capability.c 
src/celix_requirement.c)
+set(RCM_PUBLIC_LIBS Celix::utils)
+SET(RCM_PRIVATE_LIBS )
 
-add_subdirectory(framework)
+add_library(rcm STATIC ${RCM_SOURCES})
+set_target_properties(rcm PROPERTIES
+        OUTPUT_NAME "celix_rcm"
+        VERSION 0.0.1
+        SOVERSION 0)
+target_link_libraries(rcm PUBLIC ${RCM_PUBLIC_LIBS} PRIVATE 
${RCM_PRIVATE_LIBS})
+target_include_directories(rcm PRIVATE src PUBLIC include)
+celix_target_hide_symbols(rcm)
+add_library(Celix::rcm ALIAS rcm)
 
-#launcher
-add_subdirectory(launcher)
+if (ENABLE_TESTING)
+    add_library(rcm_cut STATIC ${RCM_SOURCES})
+    target_link_libraries(rcm_cut PUBLIC ${RCM_PUBLIC_LIBS} PRIVATE 
${RCM_PRIVATE_LIBS})
+    target_include_directories(rcm_cut PRIVATE src PUBLIC include)
 
-#add_subdirectory(event_admin)# event_admin is unstable
-add_subdirectory(dependency_manager)
-if (CELIX_CXX14)
-    add_subdirectory(dependency_manager_cxx)
+    add_subdirectory(gtest)
 endif ()
-
-# Error Injectors
-if (ENABLE_TESTING AND LINKER_WRAP_SUPPORTED)
-    add_subdirectory(error_injector)
-endif ()
-
diff --git a/libs/rcm/README.md b/libs/rcm/README.md
new file mode 100644
index 00000000..83c8b9c5
--- /dev/null
+++ b/libs/rcm/README.md
@@ -0,0 +1,51 @@
+---
+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.
+
+Warning: This library is still under development and not yet ready for 
production use.
+
+## TODOs
+
+ - Wiring
+ - Resolver
+
+## 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/rcm/gtest/CMakeLists.txt b/libs/rcm/gtest/CMakeLists.txt
new file mode 100644
index 00000000..943747c8
--- /dev/null
+++ b/libs/rcm/gtest/CMakeLists.txt
@@ -0,0 +1,38 @@
+# 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.
+
+set(CMAKE_CXX_STANDARD 17)
+
+add_executable(test_rcm
+    src/RequirementCapabilityModelTestSuite.cc
+)
+
+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 ..)
+
+if (LINKER_WRAP_SUPPORTED)
+    add_executable(test_rcm_with_error_injection
+        src/RequirementCapabilityModelWithErrorInjectionTestSuite.cc
+    )
+    target_link_libraries(test_rcm_with_error_injection PRIVATE rcm_cut 
GTest::gtest GTest::gtest_main)
+    target_link_libraries(test_rcm_with_error_injection PRIVATE 
Celix::malloc_ei Celix::utils_ei Celix::array_list_ei Celix::asprintf_ei)
+    target_include_directories(test_rcm_with_error_injection PRIVATE 
${CMAKE_CURRENT_SOURCE_DIR}/../src) #private headers
+    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 ..)
+endif ()
+
diff --git a/libs/rcm/gtest/src/RequirementCapabilityModelTestSuite.cc 
b/libs/rcm/gtest/src/RequirementCapabilityModelTestSuite.cc
new file mode 100644
index 00000000..ddf2943b
--- /dev/null
+++ b/libs/rcm/gtest/src/RequirementCapabilityModelTestSuite.cc
@@ -0,0 +1,263 @@
+/*
+ * 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_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));
+
+    //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_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_t* req2 = celix_requirement_create(nullptr, 
"test-namespace", nullptr);
+    ASSERT_TRUE(req2 != nullptr);
+    EXPECT_EQ(0, 
celix_properties_size(celix_requirement_getDirectives(req2))); //0 filter 
directive, because no filter is set on creation
+
+
+    celix_requirement_destroy(req);
+    celix_requirement_destroy(req2);
+}
+
+TEST_F(RequirementCapabilityModelTestSuite, TestRequirementEqualsAndHashCode) {
+    celix_requirement_t* req = celix_requirement_create(nullptr, 
"test-namespace", "(&(capability.attribute1=foo)(capability.attribute2=bar))");
+    ASSERT_TRUE(req != nullptr);
+    celix_requirement_addAttribute(req, "test-attribute1", 
"test-attribute-value1");
+
+    EXPECT_TRUE(celix_requirement_equals(req, req));
+    EXPECT_EQ(celix_requirement_hashCode(req), 
celix_requirement_hashCode(req));
+
+    celix_requirement_t* req2 = celix_requirement_create(nullptr, 
"test-namespace", "(&(capability.attribute1=foo)(capability.attribute2=bar))");
+    ASSERT_TRUE(req2 != nullptr);
+    EXPECT_FALSE(celix_requirement_equals(req, req2));
+    EXPECT_NE(celix_requirement_hashCode(req), 
celix_requirement_hashCode(req2));
+
+    celix_requirement_addAttribute(req2, "test-attribute1", 
"test-attribute-value1");
+    EXPECT_TRUE(celix_requirement_equals(req, req2));
+    EXPECT_EQ(celix_requirement_hashCode(req), 
celix_requirement_hashCode(req2));
+
+    celix_requirement_addAttribute(req2, "test-attribute1", 
"test-attribute-value1-changed");
+    EXPECT_FALSE(celix_requirement_equals(req, req2));
+    EXPECT_NE(celix_requirement_hashCode(req), 
celix_requirement_hashCode(req2));
+
+    celix_requirement_addAttribute(req2, "test-attribute1", 
"test-attribute-value1"); //overwrite changed
+    celix_requirement_addDirective(req2, "filter", 
"(&(capability.attribute1=foo)(capability.attribute2=bar-changed))");
+    EXPECT_FALSE(celix_requirement_equals(req, req2));
+    EXPECT_NE(celix_requirement_hashCode(req), 
celix_requirement_hashCode(req2));
+
+    celix_requirement_t* req3 = celix_requirement_create(nullptr, 
"test-namespace2", nullptr);
+    ASSERT_TRUE(req3 != nullptr);
+    celix_requirement_t* req4 = celix_requirement_create(nullptr, 
"test-namespace3", nullptr);
+    ASSERT_TRUE(req4 != nullptr);
+    EXPECT_FALSE(celix_requirement_equals(req3, req4)); //different namespace
+    EXPECT_NE(celix_requirement_hashCode(req3), 
celix_requirement_hashCode(req4));
+
+    celix_requirement_destroy(req);
+    celix_requirement_destroy(req2);
+    celix_requirement_destroy(req3);
+    celix_requirement_destroy(req4);
+}
+
+
+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));
+
+    //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_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);
+}
+
+TEST_F(RequirementCapabilityModelTestSuite, TestCapabilityEqualsAndHashCode) {
+    celix_capability_t* cap = celix_capability_create(nullptr, 
"test-namespace");
+    ASSERT_TRUE(cap != nullptr);
+    celix_capability_addDirective(cap, "test-directive", 
"test-directive-value");
+    celix_capability_addAttribute(cap, "test-attribute", 
"test-attribute-value");
+
+    EXPECT_TRUE(celix_capability_equals(cap, cap));
+    EXPECT_EQ(celix_capability_hashCode(cap), celix_capability_hashCode(cap));
+
+    celix_capability_t* cap2 = celix_capability_create(nullptr, 
"test-namespace");
+    ASSERT_TRUE(cap2 != nullptr);
+    EXPECT_FALSE(celix_capability_equals(cap, cap2));
+    EXPECT_NE(celix_capability_hashCode(cap), celix_capability_hashCode(cap2));
+
+    celix_capability_addDirective(cap2, "test-directive", 
"test-directive-value");
+    celix_capability_addAttribute(cap2, "test-attribute", 
"test-attribute-value");
+    EXPECT_TRUE(celix_capability_equals(cap, cap2));
+    EXPECT_EQ(celix_capability_hashCode(cap), celix_capability_hashCode(cap2));
+
+    celix_capability_addAttribute(cap2, "test-attribute", 
"test-attribute-value-changed");
+    EXPECT_FALSE(celix_capability_equals(cap, cap2));
+    EXPECT_NE(celix_capability_hashCode(cap), celix_capability_hashCode(cap2));
+
+    celix_capability_addAttribute(cap2, "test-attribute", 
"test-attribute-value"); //overwrite changed
+    celix_capability_addDirective(cap2, "test-directive", 
"test-directive-value-changed");
+    EXPECT_FALSE(celix_capability_equals(cap, cap2));
+    EXPECT_NE(celix_capability_hashCode(cap), celix_capability_hashCode(cap2));
+
+    celix_capability_t* cap3 = celix_capability_create(nullptr, 
"test-namespace2");
+    ASSERT_TRUE(cap3 != nullptr);
+    celix_capability_t* cap4 = celix_capability_create(nullptr, 
"test-namespace3");
+    ASSERT_TRUE(cap4 != nullptr);
+    EXPECT_FALSE(celix_capability_equals(cap3, cap4)); //different namespace
+    EXPECT_NE(celix_capability_hashCode(cap3), 
celix_capability_hashCode(cap4));
+
+    celix_capability_destroy(cap);
+    celix_capability_destroy(cap2);
+    celix_capability_destroy(cap3);
+    celix_capability_destroy(cap4);
+}
+
+TEST_F(RequirementCapabilityModelTestSuite, 
TestNoNamespaceForCapabilityAndRequirement) {
+    celix_err_resetErrors();
+
+    celix_capability_t* cap = celix_capability_create(nullptr, nullptr);
+    EXPECT_TRUE(cap == nullptr);
+    const char* err = celix_err_popLastError();
+    EXPECT_TRUE(err != nullptr);
+    EXPECT_TRUE(strcasestr(err, "namespace") != nullptr) << "Error message 
should contain 'namespace' but was: " << err;
+
+    celix_requirement_t* req = celix_requirement_create(nullptr, nullptr, 
nullptr);
+    EXPECT_TRUE(req == nullptr);
+    err = celix_err_popLastError();
+    EXPECT_TRUE(err != nullptr);
+    EXPECT_TRUE(strcasestr(err, "namespace") != nullptr) << "Error message 
should contain 'namespace' but was: " << err;
+}
+
+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)));
+
+    celix_requirement_t* req = celix_requirement_create(res, "test-namespace", 
"(&(capability.attribute1=foo)(capability.attribute2=bar))");
+    EXPECT_EQ(CELIX_SUCCESS, celix_resource_addRequirement(res, req));
+    EXPECT_EQ(res, celix_requirement_getResource(req));
+    EXPECT_EQ(1, celix_arrayList_size(celix_resource_getRequirements(res, 
nullptr)));
+    req = celix_requirement_create(res, "test-namespace2", nullptr);
+    EXPECT_EQ(CELIX_SUCCESS, celix_resource_addRequirement(res, req));
+    EXPECT_EQ(2, celix_arrayList_size(celix_resource_getRequirements(res, 
nullptr)));
+
+    celix_capability_t *cap = celix_capability_create(res, "test-namespace");
+    EXPECT_EQ(CELIX_SUCCESS, celix_resource_addCapability(res, cap));
+    EXPECT_EQ(res, celix_capability_getResource(cap));
+    EXPECT_EQ(1, celix_arrayList_size(celix_resource_getCapabilities(res, 
nullptr)));
+
+    req = celix_requirement_create(res2, "test-namespace2", nullptr);
+    EXPECT_EQ(CELIX_SUCCESS, celix_resource_addRequirement(res2, req));
+    req = celix_requirement_create(res2, "test-namespace", 
"(&(capability.attribute1=foo)(capability.attribute2=bar))");
+    EXPECT_EQ(CELIX_SUCCESS, celix_resource_addRequirement(res2, req));
+    cap = celix_capability_create(res2, "test-namespace");
+    EXPECT_EQ(CELIX_SUCCESS, celix_resource_addCapability(res2, cap));
+
+    //test get capabilities/requirements by namespace
+    req = celix_requirement_create(res, "test-namespace2", nullptr);
+    cap = celix_capability_create(res, "test-namespace2");
+    EXPECT_EQ(CELIX_SUCCESS, celix_resource_addRequirement(res, req));
+    EXPECT_EQ(CELIX_SUCCESS, celix_resource_addCapability(res, cap));
+    EXPECT_EQ(3, 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_err_resetErrors();
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_resource_addCapability(res, cap));
+    EXPECT_EQ(1, celix_err_getErrorCount());
+    const char* err = celix_err_popLastError();
+    EXPECT_TRUE(strcasestr(err, "capability") != nullptr) << "Error message 
should contain 'capability' but was: " << err;
+    celix_capability_destroy(cap);
+
+    EXPECT_EQ(CELIX_ILLEGAL_ARGUMENT, celix_resource_addRequirement(res, req));
+    EXPECT_EQ(1, celix_err_getErrorCount());
+    err = celix_err_popLastError();
+    EXPECT_TRUE(strcasestr(err, "requirement") != nullptr) << "Error message 
should contain 'requirement' but was: " << err;
+    celix_requirement_destroy(req);
+
+    celix_resource_destroy(res);
+}
diff --git 
a/libs/rcm/gtest/src/RequirementCapabilityModelWithErrorInjectionTestSuite.cc 
b/libs/rcm/gtest/src/RequirementCapabilityModelWithErrorInjectionTestSuite.cc
new file mode 100644
index 00000000..fe6cb33f
--- /dev/null
+++ 
b/libs/rcm/gtest/src/RequirementCapabilityModelWithErrorInjectionTestSuite.cc
@@ -0,0 +1,133 @@
+/*
+ * 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 <memory>
+
+#include "malloc_ei.h"
+#include "celix_utils_ei.h"
+#include "celix_array_list_ei.h"
+#include "asprintf_ei.h"
+
+#include "celix_capability.h"
+#include "celix_requirement.h"
+#include "celix_resource.h"
+#include "celix_err.h"
+
+class RequirementCapabilityModelWithErrorInjectionTestSuite : public 
::testing::Test {
+public:
+    RequirementCapabilityModelWithErrorInjectionTestSuite() = default;
+
+    ~RequirementCapabilityModelWithErrorInjectionTestSuite() override {
+        celix_ei_expect_malloc(nullptr, 0, nullptr);
+        celix_ei_expect_celix_utils_strdup(nullptr, 0, nullptr);
+        celix_ei_expect_celix_arrayList_createWithOptions(nullptr, 0, nullptr);
+        celix_ei_expect_celix_arrayList_create(nullptr, 0, nullptr);
+        celix_ei_expect_celix_arrayList_add(nullptr, 0, CELIX_SUCCESS);
+        celix_ei_expect_celix_utils_strdup(nullptr, 0, nullptr);
+        celix_ei_expect_vasprintf(nullptr, 0, 0);
+        celix_err_resetErrors();
+    }
+};
+
+TEST_F(RequirementCapabilityModelWithErrorInjectionTestSuite, 
TestRequirementErrorHandling) {
+    //inject error on first malloc call from celix_requirement_create
+    celix_ei_expect_malloc((void*)celix_requirement_create, 0, nullptr);
+    celix_requirement_t* req = celix_requirement_create(nullptr, "test", 
nullptr);
+    EXPECT_EQ(nullptr, req);
+    EXPECT_EQ(1, celix_err_getErrorCount());
+
+
+    //inject error on first celix_utils_strdup call from 
celix_requirement_create
+    celix_ei_expect_celix_utils_strdup((void*)celix_requirement_create, 0, 
nullptr);
+    req = celix_requirement_create(nullptr, "test", nullptr);
+    EXPECT_EQ(nullptr, req);
+    EXPECT_EQ(2, celix_err_getErrorCount());
+}
+
+TEST_F(RequirementCapabilityModelWithErrorInjectionTestSuite, 
TestCapabilityErrorHandling) {
+    //inject error on first malloc call from celix_capability_create
+    celix_ei_expect_malloc((void*)celix_capability_create, 0, nullptr);
+    celix_capability_t* cap = celix_capability_create(nullptr, "test");
+    EXPECT_EQ(nullptr, cap);
+    EXPECT_EQ(1, celix_err_getErrorCount());
+
+
+    //inject error on first celix_utils_strdup call from 
celix_capability_create
+    celix_ei_expect_celix_utils_strdup((void*)celix_capability_create, 0, 
nullptr);
+    cap = celix_capability_create(nullptr, "test");
+    EXPECT_EQ(nullptr, cap);
+    EXPECT_EQ(2, celix_err_getErrorCount());
+}
+
+TEST_F(RequirementCapabilityModelWithErrorInjectionTestSuite, 
TestResourceErrorHandling) {
+    //inject error on first malloc call from celix_resource_create
+    celix_ei_expect_malloc((void*)celix_resource_create, 0, nullptr);
+    celix_resource_t* res = celix_resource_create();
+    EXPECT_EQ(nullptr, res);
+    EXPECT_EQ(1, celix_err_getErrorCount());
+
+    //inject error on first celix_arrayList_createWithOptions call from 
celix_resource_create
+    
celix_ei_expect_celix_arrayList_createWithOptions((void*)celix_resource_create, 
0, nullptr);
+    res = celix_resource_create();
+    EXPECT_EQ(nullptr, res);
+    EXPECT_EQ(2, celix_err_getErrorCount());
+
+    res = celix_resource_create();
+    EXPECT_NE(nullptr, res);
+    celix_capability_t* cap = celix_capability_create(res, "test");
+    EXPECT_NE(nullptr, cap);
+    celix_requirement_t* req = celix_requirement_create(res, "test", nullptr);
+    EXPECT_NE(nullptr, req);
+
+    //inject error on first celix_arrayList_create call from 
celix_resource_addCapability
+    
celix_ei_expect_celix_arrayList_create((void*)celix_resource_addCapability, 0, 
nullptr);
+    EXPECT_EQ(CELIX_ENOMEM, celix_resource_addCapability(res, cap));
+    EXPECT_EQ(3, celix_err_getErrorCount());
+
+    //inject error on first celix_arrayList_add call from 
celix_resource_addCapability
+    celix_ei_expect_celix_arrayList_add((void*)celix_resource_addCapability, 
0, CELIX_ENOMEM);
+    EXPECT_EQ(CELIX_ENOMEM, celix_resource_addCapability(res, cap));
+    EXPECT_EQ(4, celix_err_getErrorCount());
+
+    //inject error on second celix_arrayList_add call from 
celix_resource_addCapability
+    celix_ei_expect_celix_arrayList_add((void*)celix_resource_addCapability, 
0, CELIX_ENOMEM, 2);
+    EXPECT_EQ(CELIX_ENOMEM, celix_resource_addCapability(res, cap));
+    EXPECT_EQ(5, celix_err_getErrorCount());
+
+    //inject error on first celix_arrayList_create call from 
celix_resource_addRequirement
+    
celix_ei_expect_celix_arrayList_create((void*)celix_resource_addRequirement, 0, 
nullptr);
+    EXPECT_EQ(CELIX_ENOMEM, celix_resource_addRequirement(res, req));
+    EXPECT_EQ(6, celix_err_getErrorCount());
+
+    //inject error on first celix_arrayList_add call from 
celix_resource_addRequirement
+    celix_ei_expect_celix_arrayList_add((void*)celix_resource_addRequirement, 
0, CELIX_ENOMEM);
+    EXPECT_EQ(CELIX_ENOMEM, celix_resource_addRequirement(res, req));
+    EXPECT_EQ(7, celix_err_getErrorCount());
+
+    //inject error on second celix_arrayList_add call from 
celix_resource_addRequirement
+    celix_ei_expect_celix_arrayList_add((void*)celix_resource_addRequirement, 
0, CELIX_ENOMEM, 2);
+    EXPECT_EQ(CELIX_ENOMEM, celix_resource_addRequirement(res, req));
+    EXPECT_EQ(8, celix_err_getErrorCount());
+
+    celix_capability_destroy(cap);
+    celix_requirement_destroy(req);
+    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..09229115
--- /dev/null
+++ b/libs/rcm/include/celix_capability.h
@@ -0,0 +1,161 @@
+/*
+ * 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
+*/
+
+/**
+ * @brief Creates a new capability.
+ *
+ * In case of a error, an error message is added to celix_err.
+ *
+ * @param[in] resource The resource which contains the capability. Can be NULL.
+ * @param[in] ns The namespace of the capability.
+ * @return The new capability.
+ * @retval NULL If an error occurred.
+ */
+celix_capability_t* celix_capability_create(
+        const celix_resource_t* resource,
+        const char* ns);
+
+/**
+ * @brief Destroys the capability.
+ * @param[in] capability The capability to destroy. Can be NULL.
+ */
+void celix_capability_destroy(celix_capability_t* capability);
+
+/**
+ * @brief Check if 2 capabilities are equal.
+ */
+bool celix_capability_equals(const celix_capability_t* cap1, const 
celix_capability_t* cap2);
+
+/**
+ * @brief Returns the hash code of the capability.
+ */
+unsigned int celix_capability_hashCode(const celix_capability_t* cap);
+
+/**
+ * @brief Returns the resource which contains the capability.
+ * @param[in] cap The capability.
+ * @return The resource or NULL if no resource is set.
+ */
+const celix_resource_t* celix_capability_getResource(const celix_capability_t* 
cap);
+
+/**
+ * @brief Returns the namespace of the capability.
+ * @param[in] cap The capability.
+ * @return The namespace of the capability.
+ */
+const char* celix_capability_getNamespace(const celix_capability_t* cap);
+
+/**
+ * @brief Returns the attributes of the capability.
+ * @param[in] cap The capability.
+ * @return The attributes of the capability. Will be an empty properties if no 
attributes are set.
+ */
+const celix_properties_t* celix_capability_getAttributes(const 
celix_capability_t* cap);
+
+/**
+ * @brief Returns the directives of the capability.
+ * @param[in] cap The capability.
+ * @return The directives of the capability. Will be an empty properties if no 
directives are set.
+ */
+const celix_properties_t* celix_capability_getDirectives(const 
celix_capability_t* cap);
+
+/**
+ * @brief Returns the value of the attribute with the given key.
+ * @param[in] cap The capability.
+ * @param[in] key The key of the attribute.
+ * @return The value of the attribute or NULL if the attribute is not set.
+ */
+const char* celix_capability_getAttribute(const celix_capability_t* cap, const 
char* key);
+
+/**
+ * @brief Returns the value of the directive with the given key.
+ * @param[in] cap The capability.
+ * @param[in] key The key of the directive.
+ * @return The value of the directive or NULL if the directive is not set.
+ */
+const char* celix_capability_getDirective(const celix_capability_t* cap, const 
char* key);
+
+/**
+ * @brief Add a new attribute to the capability.
+ *
+ * Note replaces an existing attribute is the key is already set.
+ *
+ * @param[in] cap The capability.
+ * @param[in] key The key of the attribute.
+ * @param[in] value The value of the attribute.
+ */
+void celix_capability_addAttribute(celix_capability_t* cap, const char* key, 
const char* value);
+
+/**
+ * @brief Add a new attributes to the capability.
+ *
+ * Note replaces existing attributes is some of the provided keys are already 
set.
+ *
+ * @param[in] cap The capability.
+ * @param[in] attributes The attributes to add.
+ *                       Note the attributes are copied, so the caller is 
still owner of the properties.
+ */
+void celix_capability_addAttributes(celix_capability_t* cap, const 
celix_properties_t* attributes);
+
+/**
+ * @brief Add a new directive to the capability.
+ *
+ * Note replaces an existing directive is the key is already set.
+ *
+ * @param[in] cap The capability.
+ * @param[in] key The key of the directive.
+ * @param[in] value The value of the directive.
+ */
+void celix_capability_addDirective(celix_capability_t* cap, const char* key, 
const char* value);
+
+/**
+ * @brief Add a new directives to the capability.
+ *
+ * Note replaces existing directives is some of the provided keys are already 
set.
+ *
+ * @param[in] cap The capability.
+ * @param[in] directives The directives to add.
+ *                       Note the directives are copied, so the caller is 
still owner of the properties.
+ */
+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_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..a94a14af
--- /dev/null
+++ b/libs/rcm/include/celix_requirement.h
@@ -0,0 +1,173 @@
+/*
+ * 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 "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
+*/
+
+/**
+ * @brief Creates a new requirement.
+ *
+ * In case of a error, an error message is added to celix_err.
+ *
+ * @param[in] resource The resource which contains the requirement. Can be 
NULL.
+ * @param[in] ns The namespace of the requirement.
+ * @param[in] filter The filter of the requirement. Can be NULL.
+ * @return The new requirement.
+ * @retval NULL If an error occurred.
+ */
+celix_requirement_t* celix_requirement_create(
+        celix_resource_t* resource,
+        const char* ns,
+        const char* filter);
+
+
+/**
+ * @brief Destroys the requirement.
+ * @param[in] requirement The requirement to destroy. Can be NULL.
+ */
+void celix_requirement_destroy(celix_requirement_t* requirement);
+
+/**
+ * @brief Check if 2 requirements are equal.
+ */
+bool celix_requirement_equals(const celix_requirement_t* req1, const 
celix_requirement_t* req2);
+
+/**
+ * @brief Returns the hash code of the requirement.
+ */
+unsigned int celix_requirement_hashCode(const celix_requirement_t* req);
+
+/**
+ * @brief Returns the resource which contains the requirement.
+ * @param[in] req The requirement.
+ * @return The resource which contains the requirement or NULL if the 
requirement is not part of a resource.
+ */
+const celix_resource_t* celix_requirement_getResource(const 
celix_requirement_t* req);
+
+/**
+ * @brief Returns the namespace of the requirement.
+ * @param[in] req The requirement.
+ * @return The namespace of the requirement.
+ */
+const char* celix_requirement_getNamespace(const celix_requirement_t* req);
+
+/**
+ * @brief Returns the filter of the requirement.
+ * @param[in] req The requirement.
+ * @return The filter of the requirement.
+ */
+const char* celix_requirement_getFilter(const celix_requirement_t* req);
+
+/**
+ * @brief Returns the directives of the requirement.
+ * @param[in] req The requirement.
+ * @return The directives of the requirement. Will be an empty properties if 
no directives are set.
+ */
+const celix_properties_t* celix_requirement_getDirectives(const 
celix_requirement_t* req);
+
+/**
+ * @brief Returns the value of the directive with the given key.
+ * @param[in] req The requirement.
+ * @param[in] key The key of the directive.
+ * @return The value of the directive or NULL if the directive is not set.
+ */
+const char* celix_requirement_getDirective(const celix_requirement_t* req, 
const char* key);
+
+/**
+ * @brief Returns the attributes of the requirement.
+ * @param[in] req The requirement.
+ * @return The attributes of the requirement. Will be an empty properties if 
no attributes are set.
+ */
+const celix_properties_t* celix_requirement_getAttributes(const 
celix_requirement_t* req);
+
+/**
+ *  @brief Returns the value of the attribute with the given key.
+ * @param[in] req The requirement.
+ * @param[in] key The key of the attribute.
+ * @return The value of the attribute or NULL if the attribute is not set.
+ */
+const char* celix_requirement_getAttribute(const celix_requirement_t* req, 
const char* key);
+
+/**
+ * @brief Add a new directive to the requirement.
+ *
+ * Note replaces an existing directive is the key is already set.
+ *
+ * @param[in] req The requirement.
+ * @param[in] key The key of the directive.
+ * @param[in] value The value of the directive.
+ */
+void celix_requirement_addDirective(celix_requirement_t* req, const char* key, 
const char* value);
+
+/**
+ * @brief Add a new directives to the requirement.
+ *
+ * Note replaces existing directives is some of the provided keys are already 
set.
+ *
+ * @param[in] req The requirement.
+ * @param[in] directives The directives to add.
+ *                       Note the directives are copied, so the caller is 
still owner of the properties.
+ */
+void celix_requirement_addDirectives(celix_requirement_t* req, const 
celix_properties_t* directives);
+
+/**
+ * @brief Add a new attribute to the requirement.
+ *
+ * Note replaces an existing attribute is the key is already set.
+ *
+ * @param[in] req The requirement.
+ * @param[in] key The key of the attribute.
+ * @param[in] value The value of the attribute.
+ */
+void celix_requirement_addAttribute(celix_requirement_t* req, const char* key, 
const char* value);
+
+/**
+ * @brief Add a new attributes to the requirement.
+ *
+ * Note replaces existing attributes is some of the provided keys are already 
set.
+ *
+ * @param[in] req The requirement.
+ * @param[in] attributes The attributes to add.
+ *                       Note the attributes are copied, so the caller is 
still owner of the properties.
+ */
+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..11e7c98c
--- /dev/null
+++ b/libs/rcm/include/celix_resource.h
@@ -0,0 +1,112 @@
+/*
+ * 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 "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
+*/
+
+/**
+ * @brief Creates a new resource.
+ *
+ * In case of a error, an error message is added to celix_err.
+ *
+ * @return A new resource.
+ * @retval NULL If the resource could not be created.
+ */
+celix_resource_t* celix_resource_create();
+
+/**
+ * @brief Destroys the resource.
+ * @param[in] resource The resource to destroy. Can be NULL.
+ */
+void celix_resource_destroy(celix_resource_t* resource);
+
+/**
+ * @brief Returns the capabilities of the resource.
+ *
+ * Will return all resource capabilities if the provided namespace is NULL.
+ *
+ * @param[in] res The resource.
+ * @param[in] ns The namespace of the capabilities. Can be NULL.
+ * @return The capabilities of the resource. Will be an empty list if the 
resource has no capabilities or
+ *         has no capabilities with the provided namespace.
+ */
+const celix_array_list_t* celix_resource_getCapabilities(const 
celix_resource_t* res, const char* ns);
+
+/**
+ * @brief Returns the requirements of the resource for the provided namespace.
+ *
+ * Will return all resource requirements if the provided namespace is NULL.
+ *
+ * @param[in] res The resource.
+ * @param[in] ns The namespace of the requirements. Can be NULL.
+ * @return The requirements of the resource. Will be an empty list if the 
resource has no requirements or
+ *         has no requirements with the provided namespace.
+ */
+const celix_array_list_t* celix_resource_getRequirements(const 
celix_resource_t* res, const char* ns);
+
+/**
+ * @brief Adds a capability to the resource.
+ *
+ * The capability resource must be the same as this resource or a 
CELIX_ILLEGAL_ARGUMENT error is returned.
+ *
+ * In case of a error, an error message is added to celix_err.
+ *
+ * @param[in] res The resource.
+ * @param[in] cap The capability to add.
+ * @return CELIX_SUCCESS if the capability was added successfully.
+ * @retval CELIX_ILLEGAL_ARGUMENT If the capability resource is not the same 
as this resource.
+ * @retval ENOMEM If there is not enough memory to add the capability.
+ */
+celix_status_t celix_resource_addCapability(celix_resource_t* res, 
celix_capability_t* cap);
+
+/**
+ * @brief Adds a requirement to the resource.
+ *
+ * In case of a error, an error message is added to celix_err.
+ *
+ * @param[in] res The resource.
+ * @param[in] req The requirement to add.
+ * @return CELIX_SUCCESS if the requirement was added successfully.
+ * @retval CELIX_ILLEGAL_ARGUMENT If the requirement resource is not the same 
as this resource.
+ * @retval ENOMEM If there is not enough memory to add the requirement.
+ */
+celix_status_t 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..93aeaf5b
--- /dev/null
+++ b/libs/rcm/src/celix_capability.c
@@ -0,0 +1,172 @@
+/*
+ * 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_err.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_err_push("Failed to create celix_capability_t. Namespace is NULL 
or empty.");
+        return NULL;
+    }
+
+    celix_capability_t* cap = malloc(sizeof(*cap));
+    if (cap == NULL) {
+        celix_err_push("Failed to allocate celix_capability_t. Out of 
memory.");
+        return 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_err_push("Failed to allocate celix_capability_t fields. Out of 
memory.");
+        celix_capability_destroy(cap);
+        return NULL;
+    }
+
+    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;
+
+    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_requirement.c b/libs/rcm/src/celix_requirement.c
new file mode 100644
index 00000000..48f3dd45
--- /dev/null
+++ b/libs/rcm/src/celix_requirement.c
@@ -0,0 +1,185 @@
+/*
+ * 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_err.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_err_push("Failed to create celix_requirement_t. Namespace is 
NULL or empty.");
+        return NULL;
+    }
+
+    celix_requirement_t* req = malloc(sizeof(*req));
+    if (req == NULL) {
+        celix_err_push("Failed to allocate celix_requirement_t. Out of 
memory.");
+        return 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_err_push("Failed to allocate celix_requirement_t fields. Out of 
memory.");
+        celix_requirement_destroy(req);
+        return NULL;
+    }
+
+    if (filter != NULL) {
+        celix_properties_set(req->directives, 
CELIX_REQUIREMENT_DIRECTIVE_FILTER, filter);
+    }
+
+    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;
+
+    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..a1e53c57
--- /dev/null
+++ b/libs/rcm/src/celix_resource.c
@@ -0,0 +1,155 @@
+/*
+ * 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_err.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_err_push("Failed to allocate celix_resource_t. Out of memory.");
+        return 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_err_push("Failed to allocate celix_resource_t fields. Out of 
memory.");
+        celix_resource_destroy(res);
+        return NULL;
+    }
+    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);
+    }
+}
+
+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;
+}
+
+celix_status_t celix_resource_addCapability(celix_resource_t* res, 
celix_capability_t* cap) {
+    if (celix_capability_getResource(cap) != res) {
+        celix_err_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 CELIX_ILLEGAL_ARGUMENT;
+    }
+
+    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();
+        if (caps == NULL) {
+            goto err_handling;
+        }
+        celix_stringHashMap_put(res->capabilitiesByNamespace, ns, caps);
+    }
+    CELIX_GOTO_IF_ERR(celix_arrayList_add(caps, cap), err_handling);
+    CELIX_GOTO_IF_ERR(celix_arrayList_add(res->allCapabilities, cap), 
err_handling);
+    return CELIX_SUCCESS;
+err_handling:
+    celix_err_push("Failed to add capability to resource. Out of memory.");
+    if (caps != NULL) {
+        celix_arrayList_remove(caps, cap);
+    }
+    return ENOMEM;
+}
+
+celix_status_t celix_resource_addRequirement(celix_resource_t* res, 
celix_requirement_t* req) {
+    if (celix_requirement_getResource(req) != res) {
+        celix_err_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 CELIX_ILLEGAL_ARGUMENT;
+    }
+
+    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();
+        if (reqs == NULL) {
+            goto err_handling;
+        }
+        celix_stringHashMap_put(res->requirementsByNamespace, ns, reqs);
+    }
+    CELIX_GOTO_IF_ERR(celix_arrayList_add(reqs, req), err_handling);
+    CELIX_GOTO_IF_ERR(celix_arrayList_add(res->allRequirements, req), 
err_handling);
+    return CELIX_SUCCESS;
+err_handling:
+    celix_err_push("Failed to add requirement to resource. Out of memory.");
+    if (reqs != NULL) {
+        celix_arrayList_remove(reqs, req);
+    }
+    return ENOMEM;
+}

Reply via email to