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 <[email protected]>
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;
+}

Reply via email to