You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2023/05/07 12:09:19 UTC
[celix] 01/01: #551: Add 1st part of the requirement-capability-model lib
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 <pe...@gmail.com>
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:
+
+
+
+## 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;
+}